4
@brief Georectification module for GRASS GIS. Includes ground control
5
point management and interactive point and click GCP creation
18
(C) 2006-2010 by the GRASS Development Team
20
This program is free software under the GNU General Public License
21
(>=v2). Read the file COPYING that comes with GRASS for details.
23
@author Michael Barton
24
@author Updated by Martin Landa <landa.martin gmail.com>
34
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin, TextEditMixin
35
import wx.lib.colourselect as csel
36
import wx.wizard as wiz
38
import grass.script as grass
48
from debug import Debug as Debug
49
from icon import Icons as Icons
50
from location_wizard import TitledPage as TitledPage
51
from preferences import globalSettings as UserSettings
54
import subprocess # Not needed if GRASS commands could actually be quiet
56
CompatPath = globalvar.ETCWXDIR
57
sys.path.append(CompatPath)
58
from compat import subprocess
60
gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
61
sys.path.append(gmpath)
72
class GeorectWizard(object):
74
Start wizard here and finish wizard here
77
def __init__(self, parent):
78
self.parent = parent # GMFrame
81
# get environmental variables
83
p = gcmd.Command(['g.gisenv', 'get=GISDBASE'])
84
self.grassdatabase = p.ReadStdOutput()[0]
87
# read original environment settings
89
self.orig_gisrc = os.environ['GISRC']
92
f = open(self.orig_gisrc, 'r')
93
for line in f.readlines():
94
line = line.replace('\n', '').strip()
97
key, value = line.split(':', 1)
98
self.gisrc_dict[key.strip()] = value.strip()
102
self.currentlocation = self.gisrc_dict['LOCATION_NAME']
103
self.currentmapset = self.gisrc_dict['MAPSET']
104
# location for xy map to georectify
105
self.newlocation = ''
106
# mapset for xy map to georectify
109
# GISRC file for source location/mapset of map(s) to georectify
113
# define wizard pages
115
self.wizard = wiz.Wizard(parent=parent, id=wx.ID_ANY, title=_("Setup for georectification"))
116
self.startpage = LocationPage(self.wizard, self)
117
self.grouppage = GroupPage(self.wizard, self)
118
self.mappage = DispMapPage(self.wizard, self)
121
# set the initial order of the pages
123
self.startpage.SetNext(self.grouppage)
124
self.grouppage.SetPrev(self.startpage)
125
self.grouppage.SetNext(self.mappage)
126
self.mappage.SetPrev(self.grouppage)
131
self.startpage.DoLayout()
132
self.grouppage.DoLayout()
133
self.mappage.DoLayout()
134
self.wizard.FitToPage(self.startpage)
136
# self.Bind(wx.EVT_CLOSE, self.Cleanup)
137
# self.parent.Bind(wx.EVT_ACTIVATE, self.OnGLMFocus)
144
if self.wizard.RunWizard(self.startpage):
145
success = self.OnWizFinished()
147
wx.MessageBox(parent=self.parent,
148
message=_("Georectifying setup canceled."),
149
caption=_("Georectify"),
150
style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
153
wx.MessageBox(parent=self.parent,
154
message=_("Georectifying setup canceled."),
155
caption=_("Georectify"),
156
style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
160
# start display showing xymap
163
# instance of render.Map to be associated with display
164
self.Map = render.Map(gisrc=self.new_gisrc)
172
if maptype == 'cell':
173
rendertype = 'raster'
174
cmdlist = ['d.rast', 'map=%s' % xy_map]
175
else: # -> vector layer
176
rendertype = 'vector'
177
cmdlist = ['d.vect', 'map=%s' % xy_map]
179
self.Map.AddLayer(type=rendertype, command=cmdlist, l_active=True,
180
name=utils.GetLayerNameFromCmd(cmdlist),
181
l_hidden=False, l_opacity=1.0, l_render=False)
186
self.gcpmgr = GCP(self.parent, grwiz=self)
192
self.xy_mapdisp = mapdisp.MapFrame(self.gcpmgr, title=_("Set ground control points (GCPs)"),
193
size=globalvar.MAP_WINDOW_SIZE,
194
toolbars=["georect"],
195
Map=self.Map, gismgr=self.parent)
196
self.xy_mapdisp.SetTitle(_("GRASS GIS Map Display: 1" +
197
" - Location: " + self.newlocation +
198
" (source location)"))
199
self.xy_mapdisp.SetName("GRMapWindow")
201
self.gcpmgr.SetMapDisplay(self.xy_mapdisp)
203
self.mapwin = self.xy_mapdisp.MapWindow
205
# set mouse characteristics
206
self.mapwin.mouse['box'] = 'point'
207
self.mapwin.mouse["use"] == "pointer"
208
self.mapwin.zoomtype = 0
209
self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID)
210
self.mapwin.SetCursor(self.xy_mapdisp.cursors["cross"])
213
# show new display & draw map
215
self.xy_mapdisp.toolbars['georect'].OnZoomMap(None)
216
self.xy_mapdisp.CenterOnScreen()
217
self.xy_mapdisp.Show()
223
def SetSrcEnv(self, location, mapset):
224
"""Create environment to use for location and mapset
225
that are the source of the file(s) to georectify
227
@param location source location
228
@param mapset source mapset
230
@return False on error
231
@return True on success
234
self.newlocation = location
235
self.newmapset = mapset
237
# check to see if we are georectifying map in current working location/mapset
238
if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
241
self.gisrc_dict['LOCATION_NAME'] = location
242
self.gisrc_dict['MAPSET'] = mapset
244
self.new_gisrc = utils.GetTempfile()
247
f = open(self.new_gisrc, mode='w')
248
for line in self.gisrc_dict.items():
249
f.write(line[0] + ": " + line[1] + "\n")
255
def SwitchEnv(self, grc):
257
Switches between original working location/mapset and
258
location/mapset that is source of file(s) to georectify
260
# check to see if we are georectifying map in current working location/mapset
261
if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
264
if grc == 'original':
265
os.environ["GISRC"] = str(self.orig_gisrc)
267
os.environ["GISRC"] = str(self.new_gisrc)
271
def OnWizFinished(self):
276
def OnGLMFocus(self, event):
277
"""Layer Manager focus"""
278
# self.SwitchEnv('original')
283
"""Return to current location and mapset"""
284
self.SwitchEnv('original')
286
self.parent.georectifying = None
288
if hasattr(self, "xy_mapdisp"):
289
self.xy_mapdisp.Close()
290
self.xy_mapdisp = None
292
self.wizard.Destroy()
294
# clear GCPs from target display
295
self.parent.curr_page.maptree.mapdisplay.MapWindow.UpdateMap(render=False)
297
class LocationPage(TitledPage):
299
Set map type (raster or vector) to georectify and
300
select location/mapset of map(s) to georectify.
302
def __init__(self, wizard, parent):
303
TitledPage.__init__(self, wizard, _("Select map type and location/mapset"))
306
self.grassdatabase = self.parent.grassdatabase
311
tmplist = os.listdir(self.grassdatabase)
316
# create a list of valid locations
319
if os.path.isdir(os.path.join(self.grassdatabase, item)) and \
320
os.path.exists(os.path.join(self.grassdatabase, item, 'PERMANENT')):
321
self.locList.append(item)
323
utils.ListSortLower(self.locList)
328
self.sizer.AddGrowableCol(2)
330
self.rb_maptype = wx.RadioBox(parent=self, id=wx.ID_ANY,
331
label=' %s ' % _("Map type to georectify"),
332
choices=[_('raster'), _('vector')],
333
majorDimension=wx.RA_SPECIFY_COLS)
334
self.sizer.Add(item=self.rb_maptype,
335
flag=wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, border=5,
336
pos=(1, 1), span=(1, 2))
339
self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source location:')),
340
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
342
self.cb_location = wx.ComboBox(parent=self, id=wx.ID_ANY,
343
choices = self.locList, size=(300, -1),
344
style=wx.CB_DROPDOWN | wx.CB_READONLY)
345
self.sizer.Add(item=self.cb_location,
346
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
350
self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source mapset:')),
351
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
353
self.cb_mapset = wx.ComboBox(parent=self, id=wx.ID_ANY,
354
choices = self.mapsetList, size=(300, -1),
355
style=wx.CB_DROPDOWN | wx.CB_READONLY)
356
self.sizer.Add(item=self.cb_mapset,
357
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
363
self.Bind(wx.EVT_RADIOBOX, self.OnMaptype, self.rb_maptype)
364
self.Bind(wx.EVT_COMBOBOX, self.OnLocation, self.cb_location)
365
self.Bind(wx.EVT_COMBOBOX, self.OnMapset, self.cb_mapset)
366
self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
367
self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
368
# self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
370
def OnMaptype(self,event):
371
"""Change map type"""
374
if event.GetInt() == 0:
379
def OnLocation(self, event):
380
"""Sets source location for map(s) to georectify"""
381
self.xylocation = event.GetString()
383
#create a list of valid mapsets
384
tmplist = os.listdir(os.path.join(self.grassdatabase, self.xylocation))
387
if os.path.isdir(os.path.join(self.grassdatabase, self.xylocation, item)) and \
388
os.path.exists(os.path.join(self.grassdatabase, self.xylocation, item, 'WIND')):
389
if item != 'PERMANENT':
390
self.mapsetList.append(item)
392
self.xymapset = 'PERMANENT'
393
utils.ListSortLower(self.mapsetList)
394
self.mapsetList.insert(0, 'PERMANENT')
395
self.cb_mapset.SetItems(self.mapsetList)
396
self.cb_mapset.SetStringSelection(self.xymapset)
398
if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
399
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
401
def OnMapset(self, event):
402
"""Sets source mapset for map(s) to georectify"""
403
if self.xylocation == '':
404
wx.MessageBox(_('You must select a valid location before selecting a mapset'))
407
self.xymapset = event.GetString()
409
if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
410
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
412
def OnPageChanging(self, event=None):
413
if event.GetDirection() and \
414
(self.xylocation == '' or self.xymapset == ''):
415
wx.MessageBox(_('You must select a valid location and mapset in order to continue'))
419
self.parent.SetSrcEnv(self.xylocation, self.xymapset)
421
def OnEnterPage(self, event=None):
422
if self.xylocation == '' or self.xymapset == '':
423
wx.FindWindowById(wx.ID_FORWARD).Enable(False)
425
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
427
class GroupPage(TitledPage):
429
Set group to georectify. Create group if desired.
431
def __init__(self, wizard, parent):
432
TitledPage.__init__(self, wizard, _("Select image/map group to georectify"))
436
self.grassdatabase = self.parent.grassdatabase
444
self.extension = 'georect' + str(os.getpid())
449
self.sizer.AddGrowableCol(2)
451
self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select group:')),
452
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
454
self.cb_group = wx.ComboBox(parent=self, id=wx.ID_ANY,
455
choices=self.groupList, size=(350, -1),
456
style=wx.CB_DROPDOWN | wx.CB_READONLY)
457
self.sizer.Add(item=self.cb_group,
458
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
462
self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Create group if none exists')),
463
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
465
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
466
self.btn_mkgroup = wx.Button(parent=self, id=wx.ID_ANY, label=_("Create/edit group..."))
467
self.btn_vgroup = wx.Button(parent=self, id=wx.ID_ANY, label=_("Add vector map to group..."))
468
self.btn_vgroup.Hide()
469
btnSizer.Add(item=self.btn_mkgroup,
470
flag=wx.RIGHT, border=5)
472
btnSizer.Add(item=self.btn_vgroup,
473
flag=wx.LEFT, border=5)
475
self.sizer.Add(item=btnSizer,
476
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
480
self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Extension for output maps:')),
481
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
483
self.ext_txt = wx.TextCtrl(parent=self, id=wx.ID_ANY, value="", size=(350,-1))
484
self.ext_txt.SetValue(self.extension)
485
self.sizer.Add(item=self.ext_txt,
486
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
492
self.Bind(wx.EVT_COMBOBOX, self.OnGroup, self.cb_group)
493
self.Bind(wx.EVT_TEXT, self.OnExtension, self.ext_txt)
494
self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
495
self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
496
self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
498
def OnGroup(self, event):
499
self.xygroup = event.GetString()
501
def OnMkGroup(self, event):
502
"""Create new group in source location/mapset"""
503
menuform.GUI().ParseCommand(['i.group'],
504
completed=(self.GetOptData, None, ''),
505
parentframe=self.parent.parent, modal=True)
507
def OnVGroup(self, event):
508
"""Add vector maps to group"""
509
dlg = VectGroup(parent = self,
511
grassdb = self.grassdatabase,
512
location = self.xylocation,
513
mapset = self.xymapset,
514
group = self.xygroup)
516
if dlg.ShowModal() != wx.ID_OK:
522
def GetOptData(self, dcmd, layer, params, propwin):
523
"""Process i.group"""
531
def OnExtension(self, event):
532
self.extension = event.GetString()
534
def OnPageChanging(self, event=None):
535
if event.GetDirection() and self.xygroup == '':
536
wx.MessageBox(_('You must select a valid image/map group in order to continue'))
540
if event.GetDirection() and self.extension == '':
541
wx.MessageBox(_('You must enter an map name extension in order to continue'))
545
def OnEnterPage(self, event=None):
550
self.xylocation = self.parent.gisrc_dict['LOCATION_NAME']
551
self.xymapset = self.parent.gisrc_dict['MAPSET']
553
# create a list of groups in selected mapset
554
if os.path.isdir(os.path.join(self.grassdatabase,
558
tmplist = os.listdir(os.path.join(self.grassdatabase,
563
if os.path.isdir(os.path.join(self.grassdatabase,
568
self.groupList.append(item)
570
if maptype == 'cell':
571
self.btn_vgroup.Hide()
572
self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup)
574
elif maptype == 'vector':
575
self.btn_vgroup.Show()
576
self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup)
577
self.Bind(wx.EVT_BUTTON, self.OnVGroup, self.btn_vgroup)
579
utils.ListSortLower(self.groupList)
580
self.cb_group.SetItems(self.groupList)
582
if len(self.groupList) > 0 and \
584
self.cb_group.SetSelection(0)
585
self.xygroup = self.groupList[0]
587
if self.xygroup == '' or \
588
self.extension == '':
589
wx.FindWindowById(wx.ID_FORWARD).Enable(False)
591
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
594
self.parent.SwitchEnv('new')
596
class DispMapPage(TitledPage):
598
Select ungeoreferenced map to display for interactively
599
setting ground control points (GCPs).
601
def __init__(self, wizard, parent):
602
TitledPage.__init__(self, wizard,
603
_("Select image/map to display for ground control point (GCP) creation"))
611
self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select display image/map:')),
612
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
615
self.selection = gselect.Select(self, id=wx.ID_ANY,
616
size=globalvar.DIALOG_GSELECT_SIZE)
618
self.sizer.Add(item=self.selection,
619
flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
625
self.selection.Bind(wx.EVT_TEXT, self.OnSelection)
626
self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
627
self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
628
self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
630
def OnSelection(self,event):
631
"""Map to display selected"""
635
xy_map = event.GetString()
638
wx.FindWindowById(wx.ID_FORWARD).Enable(False)
640
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
643
# set computational region to match selected map and zoom display to region
644
if maptype == 'cell':
645
p = gcmd.RunCommand('g.region', rast = xy_map)
646
elif maptype == 'vector':
647
p = gcmd.RunCommand('g.region', vect = xy_map)
651
def OnPageChanging(self, event=None):
654
if event.GetDirection() and xy_map == '':
655
wx.MessageBox(_('You must select a valid image/map in order to continue'))
658
self.parent.SwitchEnv('original')
660
def OnEnterPage(self, event=None):
664
self.selection.SetElementList(maptype,
665
mapsets = [self.parent.newmapset, ])
668
wx.FindWindowById(wx.ID_FORWARD).Enable(False)
670
wx.FindWindowById(wx.ID_FORWARD).Enable(True)
674
Manages ground control points for georectifying. Calculates RMS statics.
675
Calls i.rectify or v.transform to georectify map.
678
def __init__(self, parent, grwiz, mapdisp=None, id=wx.ID_ANY,
679
title=_("Define/manage ground control points"),
680
size=wx.DefaultSize):
682
wx.Frame.__init__(self, parent, id, title, size=(625, 300))
684
self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
689
self.parent = parent # GMFrame
690
self.parent.georectifying = self
692
self.mapdisp = mapdisp # XY-location Map Display
693
self.grwiz = grwiz # GR Wizard
695
self.grassdatabase = self.grwiz.grassdatabase
697
self.currentlocation = self.grwiz.currentlocation
698
self.currentmapset = self.grwiz.currentmapset
700
self.newlocation = self.grwiz.newlocation
701
self.newmapset = self.grwiz.newmapset
703
self.xylocation = self.grwiz.gisrc_dict['LOCATION_NAME']
704
self.xymapset = self.grwiz.gisrc_dict['MAPSET']
705
self.xygroup = self.grwiz.grouppage.xygroup
706
self.extension = self.grwiz.grouppage.extension
710
'points' : os.path.join(self.grassdatabase,
716
'rgrp' : os.path.join(self.grassdatabase,
722
'vgrp' : os.path.join(self.grassdatabase,
728
'target' : os.path.join(self.grassdatabase,
736
# polynomial order transformation for georectification
738
# number of GCPs selected to be used for georectification (checked)
741
self.fwd_rmserror = 0.0
743
self.bkw_rmserror = 0.0
744
# list map coords and ID of map display they came from
745
self.mapcoordlist = []
746
# region clipping for georectified map
747
self.clip_to_region = False
749
self.SetTarget(self.xygroup, self.currentlocation, self.currentmapset)
752
# toolbar and display for xy map
754
self.toolbar = toolbars.GCPToolbar(parent=self, tbframe=self).GetToolbar()
755
self.SetToolBar(self.toolbar)
757
self.SetMapDisplay(self.mapdisp)
762
self.CreateStatusBar(number=1)
764
# can put guage into custom statusbar for progress if can figure out how to get progress text from i.rectify
765
# self.gr_gauge = wx.Gauge(self, -1, 100, (-1,-1), (100, 25))
766
# self.gr_guage.Pulse()
768
panel = wx.Panel(parent=self)
773
sizer = wx.BoxSizer(wx.VERTICAL)
775
self.rb_grmethod = wx.RadioBox(parent=panel, id=wx.ID_ANY,
776
label=" %s " % _("Select rectification method for rasters"),
777
choices=[_('1st order'), _('2nd order'), _('3rd order')],
778
majorDimension=wx.RA_SPECIFY_COLS)
779
sizer.Add(item=self.rb_grmethod, proportion=0,
780
flag=wx.EXPAND | wx.ALL, border=5)
782
self.check = wx.CheckBox(parent=panel, id=wx.ID_ANY,
783
label=_("clip to computational region in target location"))
784
sizer.Add(item=self.check, proportion=0,
785
flag=wx.EXPAND | wx.ALL, border=5)
787
box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
788
label=" %s " % _("Ground Control Points"))
789
boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
791
# initialize list control for GCP management
792
self.list = GCPList(parent=panel, gcp=self)
794
boxSizer.Add(item=self.list, proportion=1,
795
flag=wx.EXPAND | wx.ALL, border=3)
797
sizer.Add(item=boxSizer, proportion=1,
798
flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
803
self.Bind(wx.EVT_RADIOBOX, self.OnGRMethod, self.rb_grmethod)
804
self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
805
self.Bind(wx.EVT_CLOSE, self.OnQuit)
806
self.Bind(wx.EVT_CHECKBOX, self.ClipRegion, self.check)
808
panel.SetSizer(sizer)
812
"""Disable georectification mode"""
813
self.parent.georectifying = None
815
def ClipRegion(self, event):
816
self.clip_to_region = event.IsChecked()
818
def SetMapDisplay(self, win):
823
def SetTarget(self, tgroup, tlocation, tmapset):
825
Sets rectification target to current location and mapset
827
# check to see if we are georectifying map in current working location/mapset
828
if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
829
cmdlist = ['i.target',
833
self.grwiz.SwitchEnv('new')
834
cmdlist = ['i.target',
836
'location=%s' % tlocation,
837
'mapset=%s' % tmapset]
838
gcmd.Command(cmd=cmdlist, stderr=None)
840
self.grwiz.SwitchEnv('original')
842
def AddGCP(self, event):
844
Appends an item to GCP list
846
self.list.AddGCPItem()
847
# x, y, MapWindow instance
848
self.mapcoordlist.append({ 'gcpcoord' : (0.0, 0.0, None),
849
'mapcoord' : (0.0, 0.0, None) })
851
def DeleteGCP(self, event):
853
Deletes selected item in GCP list
855
minNumOfItems = self.OnGRMethod(None)
857
if self.list.GetItemCount() <= minNumOfItems:
858
wx.MessageBox(parent=self, message=_("At least %d GCPs required. Operation cancelled.") % minNumOfItems,
859
caption=_("Delete GCP"), style=wx.OK | wx.ICON_INFORMATION)
862
item = self.list.DeleteGCPItem()
863
del self.mapcoordlist[item]
865
def ClearGCP(self, event):
867
Clears all values in selected item of GCP list and unchecks it
869
index = self.list.GetSelected()
872
self.list.SetStringItem(index, i, '0.0')
873
self.list.SetStringItem(index, 4, '')
874
self.list.SetStringItem(index, 5, '')
875
self.list.CheckItem(index, False)
877
self.mapcoordlist[index] = { 'gcpcoord' : (0.0, 0.0, None),
878
'mapcoord' : (0.0, 0.0, None) }
880
def DrawGCP(self, coordtype):
882
Updates GCP and map coord maps and redraws
883
active (checked) GCP markers
885
col = UserSettings.Get(group='georect', key='symbol', subkey='color')
886
wxCol = wx.Colour(col[0], col[1], col[2], 255)
887
wpx = UserSettings.Get(group='georect', key='symbol', subkey='width')
888
font = self.GetFont()
890
penOrig = polypenOrig = None
893
for gcp in self.mapcoordlist:
894
mapWin = gcp[coordtype][2]
895
if not self.list.IsChecked(idx) or not mapWin:
901
polypenOrig = mapWin.polypen
902
mapWin.pen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID)
903
mapWin.polypen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID) # ?
905
coord = mapWin.Cell2Pixel((gcp[coordtype][0], gcp[coordtype][1]))
906
mapWin.DrawCross(pdc=mapWin.pdcTmp, coords=coord,
907
size=5, text={ 'text' : '%s' % str(idx + 1),
911
'coords': [coord[0] + 5,
919
mapWin.polypen = polypenOrig
921
def SetGCPData(self, coordtype, coord, mapdisp=None, check=True):
923
Inserts coordinates from mouse click on map
924
into selected item of GCP list and checks it for use
927
index = self.list.GetSelected()
928
if index == wx.NOT_FOUND:
931
coord0 = str(coord[0])
932
coord1 = str(coord[1])
934
if coordtype == 'gcpcoord':
935
self.list.SetStringItem(index, 0, coord0)
936
self.list.SetStringItem(index, 1, coord1)
937
self.mapcoordlist[index]['gcpcoord'] = (coord[0], coord[1], mapdisp)
938
elif coordtype == 'mapcoord':
939
self.list.SetStringItem(index, 2, coord0)
940
self.list.SetStringItem(index, 3, coord1)
942
self.mapcoordlist[index][coordtype] = (coord[0], coord[1], mapdisp)
944
self.list.CheckItem(index, check)
946
# self.list.ResizeColumns()
948
def SaveGCPs(self, event):
950
Make a POINTS file or save GCP coordinates to existing POINTS file
955
f = open(self.file['points'], mode='w')
956
# use os.linesep or '\n' here ???
957
f.write('# Ground Control Points File\n')
959
f.write("# target location: " + self.currentlocation + '\n')
960
f.write("# target mapset: " + self.currentmapset + '\n')
961
f.write("#unrectified xy georectified east north 1=use gcp point\n")
962
f.write("#-------------- ----------------------- ---------------\n")
964
for index in range(self.list.GetItemCount()):
965
if self.list.IsChecked(index) == True:
970
coord0 = self.list.GetItem(index, 0).GetText()
971
coord1 = self.list.GetItem(index, 1).GetText()
972
coord2 = self.list.GetItem(index, 2).GetText()
973
coord3 = self.list.GetItem(index, 3).GetText()
974
f.write(coord0 + ' ' + coord1 + ' ' + coord2 + ' ' + coord3 + ' ' + check + '\n')
976
self.parent.goutput.WriteLog(_('POINTS file <%s> saved') % self.file['points'])
977
self.SetStatusText(_('POINTS file saved'))
979
wx.MessageBox(parent=self,
980
message="%s <%s>. %s%s" % (_("Writing POINTS file failed"),
981
self.file['points'], os.linesep, err),
982
caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
989
Reads GCPs and georectified coordinates from POINTS file
994
sourceMapWin = self.mapdisp.MapWindow
995
targetMapWin = self.parent.curr_page.maptree.mapdisplay.MapWindow
998
f = open(self.file['points'], 'r')
1001
for line in f.readlines():
1002
if line[0] == '#' or line =='':
1004
line = line.replace('\n', '').strip()
1005
coords = map(float, line.split())
1011
index = self.AddGCP(event=None)
1012
self.SetGCPData('gcpcoord', (coords[0], coords[1]), sourceMapWin, check)
1013
self.SetGCPData('mapcoord', (coords[2], coords[3]), targetMapWin, check)
1015
except IOError, err:
1016
wx.MessageBox(parent=self,
1017
message="%s <%s>. %s%s" % (_("Reading POINTS file failed"),
1018
self.file['points'], os.linesep, err),
1019
caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
1025
# draw GCPs (source and target)
1027
sourceMapWin.UpdateMap(render=False, renderVector=False)
1029
targetMapWin.UpdateMap(render=False, renderVector=False)
1034
# FIXME auto calculation on load is not working
1035
#if self.CheckGCPcount():
1036
# self.RMSError(self.xygroup, self.gr_order)
1038
def ReloadGCPs(self, event):
1039
"""Reload data from file"""
1040
self.list.LoadData()
1042
def OnFocus(self, event):
1043
# self.grwiz.SwitchEnv('new')
1046
def OnRMS(self, event):
1050
self.RMSError(self.xygroup,self.gr_order)
1052
def CheckGCPcount(self, msg=False):
1054
Checks to make sure that the minimum number of GCPs have been defined and
1055
are active for the selected transformation order
1057
if (self.GCPcount < 3 and self.gr_order == 1) or \
1058
(self.GCPcount < 6 and self.gr_order == 2) or \
1059
(self.GCPcount < 10 and self.gr_order == 3):
1061
wx.MessageBox(parent=self,
1062
caption=_("RMS Error"),
1063
message=_('Insufficient points defined and active (checked) '
1064
'for selected rectification method.\n'
1065
'3+ points needed for 1st order,\n'
1066
'6+ points for 2nd order, and\n'
1067
'10+ points for 3rd order.'),
1068
style=wx.ICON_INFORMATION | wx.ID_OK | wx.CENTRE)
1073
def OnGeorect(self, event):
1075
Georectifies map(s) in group using i.rectify or v.transform
1080
if self.CheckGCPcount(msg=True) == False:
1083
if maptype == 'cell':
1084
self.grwiz.SwitchEnv('new')
1085
cmdlist = ['i.rectify','-a','group=%s' % self.xygroup,
1086
'extension=%s' % self.extension,'order=%s' % self.gr_order]
1087
if self.clip_to_region:
1088
cmdlist.append('-c')
1090
p = gcmd.Command(cmdlist)
1092
output = p.ReadStdOutput()
1093
error = p.ReadErrOutput()
1095
# printing to console
1098
msgtype, msg = i.split(':')
1099
if msgtype != 'GRASS_INFO_PERCENT':
1100
self.parent.goutput.WriteLog(text = _(msg), switchPage = True)
1104
self.parent.goutput.WriteLog(text = _(msg), switchPage = True)
1106
if p.returncode == 0:
1107
wx.MessageBox('Maps in group %s georectified successfully' % self.xygroup)
1109
wx.MessageBox('ERROR...',error)
1112
self.grwiz.SwitchEnv('original')
1114
elif maptype == 'vector':
1116
# loop through all vectors in VREF
1117
# and move resulting vector to target location
1119
# make sure current mapset has a vector folder
1120
if not os.path.isdir(os.path.join(self.grassdatabase,
1121
self.currentlocation,
1124
os.mkdir(os.path.join(self.grassdatabase,
1125
self.currentlocation,
1129
self.grwiz.SwitchEnv('new')
1131
# make list of vectors to georectify from VREF
1132
f = open(self.file['vgrp'])
1135
for vect in f.readlines():
1136
vect = vect.strip('\n')
1139
vectlist.append(vect)
1143
# georectify each vector in VREF using v.transform
1144
for vect in vectlist:
1145
self.outname = vect + '_' + self.extension
1146
self.parent.goutput.WriteLog(text = _('Transforming <%s>...') % vect,
1149
p = gcmd.Command(['v.transform',
1152
'output=%s' % self.outname,
1153
'points=%s' % self.file['points']])
1155
output = p.ReadStdOutput()
1156
error = p.ReadErrOutput()
1158
# printing to console
1161
msgtype, msg = i.split(':')
1162
if msgtype != 'GRASS_INFO_PERCENT':
1163
self.parent.goutput.WriteLog(text = _(msg), switchPage = True)
1167
self.parent.goutput.WriteLog(text = _(i), switchPage = True)
1169
if p.returncode == 0:
1170
self.VectGRList.append(self.outname)
1172
self.parent.goutput.WriteError(_('Georectification of vector map <%s> failed') %
1176
# Copying database information not working.
1177
# Does not copy from xy location to current location
1179
# for layer in grass.vector_db(map = vect).itervalues():
1180
# xyLayer.append((layer['driver'],
1181
# layer['database'],
1185
# dbConnect = grass.db_connection()
1186
# print 'db connection =', dbConnect
1187
# for layer in xyLayer:
1188
# self.parent.goutput.RunCmd(['db.copy',
1191
# 'from_driver=%s' % layer[0],
1192
# 'from_database=%s' % layer[1],
1193
# 'from_table=%s' % layer[2],
1194
# 'to_driver=%s' % dbConnect['driver'],
1195
# 'to_database=%s' % dbConnect['database'],
1196
# 'to_table=%s' % layer[2] + '_' + self.extension])
1198
# copy all georectified vectors from source location to current location
1199
for name in self.VectGRList:
1200
xyvpath = os.path.join(self.grassdatabase,
1205
vpath = os.path.join(self.grassdatabase,
1206
self.currentlocation,
1211
if os.path.isdir(vpath):
1212
self.parent.goutput.WriteWarning(_('Vector map <%s> already exists. '
1213
'Change extension name and '
1214
'georectify again.') % self.outname)
1217
shutil.move(xyvpath, vpath)
1219
wx.MessageBox('For all vector maps georectified successfully, ' + '\n' +
1220
'you will need to copy any associated attribute tables' + '\n' +
1221
'and reconnect them to the georectified vectors')
1223
self.grwiz.SwitchEnv('original')
1225
def OnSettings(self, event):
1226
"""Georectifier settings"""
1227
dlg = GrSettingsDialog(parent=self, id=wx.ID_ANY, title=_('Georectifier settings'))
1229
if dlg.ShowModal() == wx.ID_OK:
1234
def OnQuit(self, event):
1235
"""Quit georectifier"""
1236
self.grwiz.Cleanup()
1242
def OnGRMethod(self, event):
1244
sets transformation order for georectifying
1247
self.gr_order = event.GetInt() + 1
1249
numOfItems = self.list.GetItemCount()
1250
minNumOfItems = numOfItems
1252
if self.gr_order == 1:
1254
# self.SetStatusText(_('Insufficient points, 3+ points needed for 1st order'))
1256
elif self.gr_order == 2:
1258
diff = 6 - numOfItems
1259
# self.SetStatusText(_('Insufficient points, 6+ points needed for 2nd order'))
1261
elif self.gr_order == 3:
1263
# self.SetStatusText(_('Insufficient points, 10+ points needed for 3rd order'))
1265
for i in range(minNumOfItems - numOfItems):
1268
return minNumOfItems
1270
def RMSError(self, xygroup, order):
1272
Uses g.transform to calculate forward and backward error for each used GCP
1273
in POINTS file and insert error values into GCP list.
1274
Calculates total forward and backward RMS error for all used points
1276
# save GCPs to points file to make sure that all checked GCPs are used
1279
if self.CheckGCPcount(msg=True) == False:
1282
# get list of forward and reverse rms error values for each point
1283
self.grwiz.SwitchEnv('new')
1285
p = gcmd.Command(['g.transform',
1286
'group=%s' % xygroup,
1287
'order=%s' % order])
1289
self.grwiz.SwitchEnv('original')
1291
errlist = p.ReadStdOutput()
1295
# insert error values into GCP list for checked items
1300
for index in range(self.list.GetItemCount()):
1301
if self.list.IsChecked(index):
1302
fwd_err, bkw_err = errlist[i].split()
1303
self.list.SetStringItem(index, 4, fwd_err)
1304
self.list.SetStringItem(index, 5, bkw_err)
1305
sumsq_fwd_err += float(fwd_err)**2
1306
sumsq_bkw_err += float(bkw_err)**2
1309
self.list.SetStringItem(index, 4, '')
1310
self.list.SetStringItem(index, 5, '')
1312
# calculate RMS error
1313
self.fwd_rmserror = round((sumsq_fwd_err/i)**0.5,4)
1314
self.bkw_rmserror = round((sumsq_bkw_err/i)**0.5,4)
1315
self.list.ResizeColumns()
1317
self.SetStatusText(_('RMS error for selected points forward: %(fwd)s backward: %(bkw)s') % \
1318
{ 'fwd' : self.fwd_rmserror, 'bkw' : self.bkw_rmserror })
1320
class GCPList(wx.ListCtrl,
1322
ListCtrlAutoWidthMixin):
1324
def __init__(self, parent, gcp, id=wx.ID_ANY,
1325
pos=wx.DefaultPosition, size=wx.DefaultSize,
1326
style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES |
1329
wx.ListCtrl.__init__(self, parent, id, pos, size, style)
1332
CheckListCtrlMixin.__init__(self)
1333
ListCtrlAutoWidthMixin.__init__(self)
1334
# TextEditMixin.__init__(self)
1336
self.gcp = gcp # GCP class
1338
# tracks whether list items are checked or not
1341
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
1342
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
1346
self.selected = wx.NOT_FOUND
1350
for col in (_('use| X coord'),
1355
_('Backward error')):
1356
self.InsertColumn(idx_col, col)
1360
"""Load data into list"""
1361
self.DeleteAllItems()
1363
if os.path.isfile(self.gcp.file['points']):
1368
self.gcp.AddGCP(None)
1370
# select first point by default
1372
self.SetItemState(self.selected,
1373
wx.LIST_STATE_SELECTED,
1374
wx.LIST_STATE_SELECTED)
1376
self.ResizeColumns()
1378
def OnCheckItem(self, index, flag):
1379
"""Item is checked/unchecked"""
1382
def AddGCPItem(self):
1384
Appends an item to GCP list
1393
self.selected = self.GetItemCount() - 1
1395
self.SetItemState(self.selected,
1396
wx.LIST_STATE_SELECTED,
1397
wx.LIST_STATE_SELECTED)
1399
self.ResizeColumns()
1401
return self.selected
1403
def DeleteGCPItem(self):
1405
Deletes selected item in GCP list
1407
if self.selected == wx.NOT_FOUND:
1410
self.DeleteItem(self.selected)
1412
if self.GetItemCount() > 0:
1413
self.selected = self.GetItemCount() - 1
1414
self.SetItemState(self.selected,
1415
wx.LIST_STATE_SELECTED,
1416
wx.LIST_STATE_SELECTED)
1418
self.selected = wx.NOT_FOUND
1420
return self.selected
1422
def ResizeColumns(self):
1423
"""Resize columns"""
1425
for i in range(self.GetColumnCount()):
1426
self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
1427
if self.GetColumnWidth(i) < minWidth:
1428
self.SetColumnWidth(i, minWidth)
1430
self.SendSizeEvent()
1432
def GetSelected(self):
1433
"""Get index of selected item"""
1434
return self.selected
1436
def OnItemSelected(self, event):
1437
self.selected = event.GetIndex()
1439
def OnItemActivated(self, event):
1441
When item double clicked, open editor to update coordinate values
1444
index = event.GetIndex()
1447
coords.append(self.GetItem(index, i).GetText())
1449
dlg = EditGPC(parent=self, id=wx.ID_ANY, data=coords)
1451
if dlg.ShowModal() == wx.ID_OK:
1452
values = dlg.GetValues() # string
1454
if len(values) == 0:
1455
wx.MessageBox(parent=self,
1456
caption=_("Edit GCP"),
1457
message=_("Invalid coordinate value. Operation cancelled."),
1458
style=wx.CENTRE | wx.ICON_ERROR | wx.ID_OK)
1460
for i in range(len(values)):
1461
if values[i] != coords[i]:
1462
self.SetStringItem(index, i, values[i])
1463
mapdisp = self.gcp.mapcoordlist[index]['gcpcoord'][2]
1464
self.gcp.mapcoordlist[index]['gcpcoord'] = (float(values[0]), float(values[1]), mapdisp)
1465
mapdisp = self.gcp.mapcoordlist[index]['mapcoord'][2]
1466
self.gcp.mapcoordlist[index]['mapcoord'] = (float(values[0]), float(values[1]), mapdisp)
1468
class VectGroup(wx.Dialog):
1470
Dialog to create a vector group (VREF file) for georectifying
1472
@todo Replace by g.group
1474
def __init__(self, parent, id, grassdb, location, mapset, group,
1475
style=wx.DEFAULT_DIALOG_STYLE):
1477
wx.Dialog.__init__(self, parent, id, style=style,
1478
title = _("Create vector map group"))
1480
self.grassdatabase = grassdb
1481
self.xylocation = location
1482
self.xymapset = mapset
1483
self.xygroup = group
1486
# get list of valid vector directories
1488
vectlist = os.listdir(os.path.join(self.grassdatabase,
1492
for dir in vectlist:
1493
if not os.path.isfile(os.path.join(self.grassdatabase,
1499
vectlist.remove(dir)
1501
utils.ListSortLower(vectlist)
1504
self.vgrpfile = os.path.join(self.grassdatabase,
1514
self.btnCancel = wx.Button(parent = self,
1516
self.btnOK = wx.Button(parent = self,
1518
self.btnOK.SetDefault()
1522
# list of vector maps
1524
self.listMap = wx.CheckListBox(parent = self, id = wx.ID_ANY,
1527
if os.path.isfile(self.vgrpfile):
1528
f = open(self.vgrpfile)
1531
for line in f.readlines():
1532
line = line.replace('\n', '')
1535
checked.append(line)
1536
self.listMap.SetCheckedStrings(checked)
1540
line = wx.StaticLine(parent = self,
1541
id = wx.ID_ANY, size = (20, -1),
1542
style = wx.LI_HORIZONTAL)
1547
sizer = wx.BoxSizer(wx.VERTICAL)
1549
box = wx.BoxSizer(wx.HORIZONTAL)
1550
box.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1551
label = _('Select vector map(s) to add to group:')),
1552
flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
1555
box.Add(item = self.listMap,
1556
flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
1560
sizer.Add(box, flag = wx.ALIGN_RIGHT | wx.ALL,
1563
sizer.Add(item = line, proportion = 0,
1564
flag = wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
1568
btnSizer = wx.StdDialogButtonSizer()
1569
btnSizer.AddButton(self.btnCancel)
1570
btnSizer.AddButton(self.btnOK)
1573
sizer.Add(item = btnSizer, proportion = 0,
1574
flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER,
1577
self.SetSizer(sizer)
1581
def MakeVGroup(self):
1582
"""Create VREF file"""
1584
for item in range(self.listMap.GetCount()):
1585
if not self.listMap.IsChecked(item):
1587
vgrouplist.append(self.listMap.GetString(item))
1589
f = open(self.vgrpfile, mode='w')
1591
for vect in vgrouplist:
1592
f.write(vect + '\n')
1596
class EditGPC(wx.Dialog):
1597
def __init__(self, parent, data, id=wx.ID_ANY,
1598
title=_("Edit GCP"),
1599
style=wx.DEFAULT_DIALOG_STYLE):
1600
"""Dialog for editing GPC and map coordinates in list control"""
1602
wx.Dialog.__init__(self, parent, id, title=title, style=style)
1604
panel = wx.Panel(parent=self)
1606
sizer = wx.BoxSizer(wx.VERTICAL)
1608
box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
1609
label=" %s " % _("Ground Control Point"))
1610
boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
1612
# source coordinates
1613
gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
1615
self.xcoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
1616
self.ycoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
1617
self.ncoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
1618
self.ecoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
1623
for label, win in ((_("X:"), self.xcoord),
1624
(_("Y:"), self.ycoord),
1625
(_("E:"), self.ecoord),
1626
(_("N:"), self.ncoord)):
1627
label = wx.StaticText(parent=panel, id=wx.ID_ANY,
1629
gridSizer.Add(item=label,
1630
flag=wx.ALIGN_CENTER_VERTICAL,
1634
win.SetValue(str(data[idx]))
1636
gridSizer.Add(item=win,
1646
boxSizer.Add(item=gridSizer, proportion=1,
1647
flag=wx.EXPAND | wx.ALL, border=5)
1649
sizer.Add(item=boxSizer, proportion=1,
1650
flag=wx.EXPAND | wx.ALL, border=5)
1655
self.btnCancel = wx.Button(panel, wx.ID_CANCEL)
1656
self.btnOk = wx.Button(panel, wx.ID_OK)
1657
self.btnOk.SetDefault()
1659
btnSizer = wx.StdDialogButtonSizer()
1660
btnSizer.AddButton(self.btnCancel)
1661
btnSizer.AddButton(self.btnOk)
1664
sizer.Add(item=btnSizer, proportion=0,
1665
flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
1667
panel.SetSizer(sizer)
1670
def GetValues(self, columns=None):
1671
"""Return list of values (as strings).
1675
float(self.xcoord.GetValue())
1676
float(self.ycoord.GetValue())
1677
float(self.ecoord.GetValue())
1678
float(self.ncoord.GetValue())
1682
valuelist.append(self.xcoord.GetValue())
1683
valuelist.append(self.ycoord.GetValue())
1684
valuelist.append(self.ecoord.GetValue())
1685
valuelist.append(self.ncoord.GetValue())
1689
class GrSettingsDialog(wx.Dialog):
1690
def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
1691
style=wx.DEFAULT_DIALOG_STYLE):
1692
wx.Dialog.__init__(self, parent, id, title, pos, size, style)
1694
Dialog to set profile text options: font, title
1695
and font size, axis labels and font size
1698
# initialize variables
1700
self.parent = parent
1706
def _do_layout(self):
1709
sizer = wx.BoxSizer(wx.VERTICAL)
1711
box = wx.StaticBox(parent=self, id=wx.ID_ANY,
1712
label=" %s " % _("Symbol settings"))
1713
boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
1714
gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
1715
gridSizer.AddGrowableCol(1)
1721
label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Color:"))
1722
gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
1723
col = UserSettings.Get(group='georect', key='symbol', subkey='color')
1724
colWin = csel.ColourSelect(parent=self, id=wx.ID_ANY,
1725
colour=wx.Colour(col[0],
1729
self.symbol['color'] = colWin.GetId()
1730
gridSizer.Add(item=colWin,
1731
flag=wx.ALIGN_RIGHT,
1738
label = wx.StaticText(parent=self, id=wx.ID_ANY, label=_("Width:"))
1739
gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
1740
width = int(UserSettings.Get(group='georect', key='symbol', subkey='width'))
1741
widWin = wx.SpinCtrl(parent=self, id=wx.ID_ANY,
1743
widWin.SetValue(width)
1744
self.symbol['width'] = widWin.GetId()
1745
gridSizer.Add(item=widWin,
1746
flag=wx.ALIGN_RIGHT,
1749
boxSizer.Add(item=gridSizer, flag=wx.EXPAND)
1750
sizer.Add(item=boxSizer, flag=wx.EXPAND | wx.ALL, border=5)
1752
line = wx.StaticLine(parent=self, id=wx.ID_ANY, size=(20, -1), style=wx.LI_HORIZONTAL)
1753
sizer.Add(item=line, proportion=0,
1754
flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border=3)
1759
btnSave = wx.Button(self, wx.ID_SAVE)
1760
btnApply = wx.Button(self, wx.ID_APPLY)
1761
btnCancel = wx.Button(self, wx.ID_CANCEL)
1762
btnSave.SetDefault()
1765
btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
1766
btnApply.SetToolTipString(_("Apply changes for the current session"))
1767
btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
1768
btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
1769
btnSave.SetDefault()
1770
btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
1771
btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
1774
btnStdSizer = wx.StdDialogButtonSizer()
1775
btnStdSizer.AddButton(btnCancel)
1776
btnStdSizer.AddButton(btnSave)
1777
btnStdSizer.AddButton(btnApply)
1778
btnStdSizer.Realize()
1780
sizer.Add(item=btnStdSizer, proportion=0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
1782
self.SetSizer(sizer)
1785
def UpdateSettings(self):
1786
UserSettings.Set(group='georect', key='symbol', subkey='color',
1787
value=wx.FindWindowById(self.symbol['color']).GetColour())
1788
UserSettings.Set(group='georect', key='symbol', subkey='width',
1789
value=wx.FindWindowById(self.symbol['width']).GetValue())
1791
def OnSave(self, event):
1792
"""Button 'Save' pressed"""
1793
self.UpdateSettings()
1795
UserSettings.ReadSettingsFile(settings=fileSettings)
1796
fileSettings['georect'] = UserSettings.Get(group='georect')
1797
file = UserSettings.SaveToFile(fileSettings)
1798
self.parent.parent.goutput.WriteLog(_('Georectifier settings saved to file \'%s\'.') % file)
1801
def OnApply(self, event):
1802
"""Button 'Apply' pressed"""
1803
self.UpdateSettings()
1806
def OnCancel(self, event):
1807
"""Button 'Cancel' pressed"""