123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799 |
- """!
- @package wxvdigit.py
- @brief wxGUI vector digitizer (base class)
- Code based on wxVdigit C++ component from GRASS 6.4.0. Converted to
- Python in 2010/12-2011/01.
- List of classes:
- - IVDigit
- (C) 2007-2011 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- @author Martin Landa <landa.martin gmail.com>
- """
- from debug import Debug
- from preferences import globalSettings as UserSettings
- from wxvdriver import DisplayDriver
- from grass.lib.grass import *
- from grass.lib.vector import *
- class IVDigit:
- def __init__(self, mapwindow):
- self.map = None
- self.mapWindow = mapwindow
-
- if not mapwindow.parent.IsStandalone():
- self.log = mapwindow.parent.GetLayerManager().goutput.cmd_stderr
- else:
- self.log = sys.stderr
-
- self.toolbar = mapwindow.parent.toolbars['vdigit']
-
- self._display = DisplayDriver(device = mapwindow.pdcVector,
- deviceTmp = mapwindow.pdcTmp,
- mapObj = mapwindow.Map,
- log = self.log)
-
- # self.SetCategory()
-
- # layer / max category
- self.cats = dict()
- # settings
- self._settings = {
- 'breakLines' : None,
- 'addCentroid' : None,
- 'catBoundary' : None
- }
- # undo/redo
- self.changesets = dict()
- self.changesetCurrent = None # first changeset to apply
- self.changesetEnd = None # last changeset to be applied
-
- if self._display.mapInfo:
- self.InitCats()
-
- # initial value for undo/redo
- self.changesetEnd = self.changesetCurrent = -1
-
- def __del__(self):
- pass # free changesets ?
- def _setCategory(self):
- pass
-
- def _openBackgroundVectorMap(self):
- pass
- def _breakLineAtIntersection(self):
- pass
-
- def _addActionsBefore(self):
- """!Register action before operation
-
- @return changeset id
- """
- pass
-
- def _addActionsAfter(self):
- pass
- def _addActionToChangeset(self):
- pass
- def _applyChangeset(self):
- pass
- def _freeChangeset(self):
- pass
- def _removeActionFromChangeset(self):
- pass
- def AddPoint (self, map, point, x, y, z=None):
- """!Add new point/centroid
- @param map map name (unused, for compatability with VEdit)
- @param point feature type (if true point otherwise centroid)
- @param x,y,z coordinates
- """
- if UserSettings.Get(group='vdigit', key="categoryMode", subkey='selection') == 2:
- layer = -1 # -> no category
- cat = -1
- else:
- layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
- cat = self.SetCategory()
-
- if point:
- type = wxvdigit.GV_POINT
- else:
- type = wxvdigit.GV_CENTROID
- snap, thresh = self.__getSnapThreshold()
- bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
- subkey='value', internal=True))
- if z:
- ret = self.digit.AddLine(type, [x, y, z], layer, cat,
- bgmap, snap, thresh)
- else:
- ret = self.digit.AddLine(type, [x, y], layer, cat,
- bgmap, snap, thresh)
- self.toolbar.EnableUndo()
- return ret
-
- def AddLine (self, map, line, coords):
- """!Add line/boundary
- @param map map name (unused, for compatability with VEdit)
- @param line feature type (if True line, otherwise boundary)
- @param coords list of coordinates
- """
- if len(coords) < 2:
- return
-
- if UserSettings.Get(group='vdigit', key="categoryMode", subkey='selection') == 2:
- layer = -1 # -> no category
- cat = -1
- else:
- layer = UserSettings.Get(group='vdigit', key="layer", subkey='value')
- cat = self.SetCategory()
-
- if line:
- type = wxvdigit.GV_LINE
- else:
- type = wxvdigit.GV_BOUNDARY
-
- listCoords = []
- for c in coords:
- for x in c:
- listCoords.append(x)
-
- snap, thresh = self.__getSnapThreshold()
-
- bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
- subkey='value', internal=True))
-
- ret = self.digit.AddLine(type, listCoords, layer, cat,
- bgmap, snap, thresh)
- self.toolbar.EnableUndo()
-
- return ret
-
- def DeleteSelectedLines(self):
- """!Delete selected features
- @return number of deleted lines
- """
- nlines = self.digit.DeleteLines(UserSettings.Get(group='vdigit', key='delRecord', subkey='enabled'))
-
- if nlines > 0:
- self.toolbar.EnableUndo()
-
- return nlines
- def MoveSelectedLines(self, move):
- """!Move selected features
- @param move direction (x, y)
- """
- snap, thresh = self.__getSnapThreshold()
-
- bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
- subkey='value', internal=True))
-
- try:
- nlines = self.digit.MoveLines(move[0], move[1], 0.0, # TODO 3D
- bgmap, snap, thresh)
- except SystemExit:
- pass
-
- if nlines > 0:
- self.toolbar.EnableUndo()
-
- return nlines
- def MoveSelectedVertex(self, coords, move):
- """!Move selected vertex of the line
- @param coords click coordinates
- @param move X,Y direction
- @return id of new feature
- @return 0 vertex not moved (not found, line is not selected)
- """
- snap, thresh = self.__getSnapThreshold()
- bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
- subkey='value', internal=True))
-
- moved = self.digit.MoveVertex(coords[0], coords[1], 0.0, # TODO 3D
- move[0], move[1], 0.0,
- bgmap, snap,
- self.driver.GetThreshold(type='selectThresh'), thresh)
- if moved:
- self.toolbar.EnableUndo()
- return moved
- def AddVertex(self, coords):
- """!Add new vertex to the selected line/boundary on position 'coords'
- @param coords coordinates to add vertex
- @return id of new feature
- @return 0 nothing changed
- @return -1 on failure
- """
- added = self.digit.ModifyLineVertex(1, coords[0], coords[1], 0.0, # TODO 3D
- self.driver.GetThreshold(type='selectThresh'))
- if added > 0:
- self.toolbar.EnableUndo()
- return added
- def RemoveVertex(self, coords):
- """!Remove vertex from the selected line/boundary on position 'coords'
- @param coords coordinates to remove vertex
- @return id of new feature
- @return 0 nothing changed
- @return -1 on failure
- """
- deleted = self.digit.ModifyLineVertex(0, coords[0], coords[1], 0.0, # TODO 3D
- self.driver.GetThreshold(type='selectThresh'))
- if deleted > 0:
- self.toolbar.EnableUndo()
- return deleted
- def SplitLine(self, coords):
- """!Split selected line/boundary on position 'coords'
- @param coords coordinates to split line
- @return 1 line modified
- @return 0 nothing changed
- @return -1 error
- """
- ret = self.digit.SplitLine(coords[0], coords[1], 0.0, # TODO 3D
- self.driver.GetThreshold('selectThresh'))
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def EditLine(self, line, coords):
- """!Edit existing line/boundary
- @param line id of line to be modified
- @param coords list of coordinates of modified line
- @return feature id of new line
- @return -1 on error
- """
- try:
- lineid = line[0]
- except:
- lineid = -1
- if len(coords) < 2:
- self.DeleteSelectedLines()
- return 0
-
- listCoords = []
- for c in coords:
- for x in c:
- listCoords.append(x)
- snap, thresh = self.__getSnapThreshold()
-
- bgmap = str(UserSettings.Get(group='vdigit', key="bgmap",
- subkey='value', internal=True))
-
- try:
- ret = self.digit.RewriteLine(lineid, listCoords,
- bgmap, snap, thresh)
- except SystemExit:
- pass
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def FlipLine(self):
- """!Flip selected lines/boundaries
- @return number of modified lines
- @return -1 on error
- """
- ret = self.digit.FlipLines()
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def MergeLine(self):
- """!Merge selected lines/boundaries
- @return number of modified lines
- @return -1 on error
- """
- ret = self.digit.MergeLines()
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def BreakLine(self):
- """!Break selected lines/boundaries
- @return number of modified lines
- @return -1 on error
- """
- ret = self.digit.BreakLines()
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def SnapLine(self):
- """!Snap selected lines/boundaries
- @return on success
- @return -1 on error
- """
- snap, thresh = self.__getSnapThreshold()
- ret = self.digit.SnapLines(thresh)
-
- if ret == 0:
- self.toolbar.EnableUndo()
- return ret
- def ConnectLine(self):
- """!Connect selected lines/boundaries
- @return 1 lines connected
- @return 0 lines not connected
- @return -1 on error
- """
- snap, thresh = self.__getSnapThreshold()
- ret = self.digit.ConnectLines(thresh)
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
-
- def CopyLine(self, ids=[]):
- """!Copy features from (background) vector map
- @param ids list of line ids to be copied
- @return number of copied features
- @return -1 on error
- """
- bgmap = str(UserSettings.Get(group='vdigit', key='bgmap',
- subkey='value', internal=True))
-
- if len(bgmap) > 0:
- ret = self.digit.CopyLines(ids, bgmap)
- else:
- ret = self.digit.CopyLines(ids, None)
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def CopyCats(self, fromId, toId, copyAttrb=False):
- """!Copy given categories to objects with id listed in ids
- @param cats ids of 'from' feature
- @param ids ids of 'to' feature(s)
- @return number of modified features
- @return -1 on error
- """
- if len(fromId) == 0 or len(toId) == 0:
- return 0
-
- ret = self.digit.CopyCats(fromId, toId, copyAttrb)
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def SelectLinesByQuery(self, pos1, pos2):
- """!Select features by query
- @param pos1, pos2 bounding box definition
- """
- thresh = self.SelectLinesByQueryThresh()
-
- w, n = pos1
- e, s = pos2
- query = wxvdigit.QUERY_UNKNOWN
- if UserSettings.Get(group='vdigit', key='query', subkey='selection') == 0:
- query = wxvdigit.QUERY_LENGTH
- else:
- query = wxvdigit.QUERY_DANGLE
- type = wxvdigit.GV_POINTS | wxvdigit.GV_LINES # TODO: 3D
-
- ids = self.digit.SelectLinesByQuery(w, n, 0.0, e, s, 1000.0,
- UserSettings.Get(group='vdigit', key='query', subkey='box'),
- query, type, thresh)
- Debug.msg(4, "VDigit.SelectLinesByQuery(): %s" % \
- ",".join(["%d" % v for v in ids]))
-
- return ids
- def GetLineCats(self, line=-1):
- """!Get layer/category pairs from given (selected) line
-
- @param line feature id (-1 for first selected line)
- """
- return dict(self.digit.GetLineCats(line))
- def GetLineLength(self, line):
- """!Get line length
- @param line feature id
- @return line length
- @return -1 on error
- """
- return self.digit.GetLineLength(line)
- def GetAreaSize(self, centroid):
- """!Get area size
- @param centroid centroid id
- @return area size
- @return -1 on error
- """
- return self.digit.GetAreaSize(centroid)
-
- def GetAreaPerimeter(self, centroid):
- """!Get area perimeter
- @param centroid centroid id
- @return area size
- @return -1 on error
- """
- return self.digit.GetAreaPerimeter(centroid)
- def SetLineCats(self, line, layer, cats, add=True):
- """!Set categories for given line and layer
- @param line feature id
- @param layer layer number (-1 for first selected line)
- @param cats list of categories
- @param add if True to add, otherwise do delete categories
- @return new feature id (feature need to be rewritten)
- @return -1 on error
- """
- ret = self.digit.SetLineCats(line, layer, cats, add)
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def GetLayers(self):
- """!Get list of layers"""
- return self.digit.GetLayers()
- def TypeConvForSelectedLines(self):
- """!Feature type conversion for selected objects.
- Supported conversions:
- - point <-> centroid
- - line <-> boundary
- @return number of modified features
- @return -1 on error
- """
- ret = self.digit.TypeConvLines()
- if ret > 0:
- self.toolbar.EnableUndo()
- return ret
- def Undo(self, level=-1):
- """!Undo action
- @param level levels to undo (0 to revert all)
- @return id of current changeset
- """
- try:
- ret = self.digit.Undo(level)
- except SystemExit:
- ret = -2
- if ret == -2:
- raise gcmd.GException(_("Undo failed, data corrupted."))
- self.mapWindow.UpdateMap(render=False)
-
- if ret < 0: # disable undo tool
- self.toolbar.EnableUndo(False)
- def ZBulkLines(self, pos1, pos2, start, step):
- """!Z-bulk labeling
- @param pos1 reference line (start point)
- @param pos1 reference line (end point)
- @param start starting value
- @param step step value
- @return number of modified lines
- @return -1 on error
- """
- ret = self.digit.ZBulkLabeling(pos1[0], pos1[1], pos2[0], pos2[1],
- start, step)
-
- if ret > 0:
- self.toolbar.EnableUndo()
-
- return ret
-
- def __getSnapThreshold(self):
- """!Get snap mode and threshold value
- @return (snap, thresh)
- """
- thresh = self.driver.GetThreshold()
- if thresh > 0.0:
- if UserSettings.Get(group='vdigit', key='snapToVertex', subkey='enabled') is True:
- snap = wxvdigit.SNAPVERTEX
- else:
- snap = wxvdigit.SNAP
- else:
- snap = wxvdigit.NO_SNAP
- return (snap, thresh)
- def GetDisplay(self):
- """!Get display driver instance"""
- return self._display
-
- def OpenMap(self, name):
- """!Open vector map for editing
-
- @param map name of vector map to be set up
- """
- Debug.msg (3, "AbstractDigit.SetMapName map=%s" % name)
- self.map = name
-
- name, mapset = name.split('@')
- try:
- ret = self._display.OpenMap(str(name), str(mapset), True)
- except SystemExit:
- ret = -1
-
- # except StandardError, e:
- # raise gcmd.GException(_("Unable to initialize display driver of vector "
- # "digitizer. See 'Command output' for details.\n\n"
- # "Details: ") + repr(e))
-
- # if map and ret == -1:
- # raise gcmd.GException(_('Unable to open vector map <%s> for editing.\n\n'
- # 'Data are probably corrupted, '
- # 'try to run v.build to rebuild '
- # 'the topology (Vector->Develop vector map->'
- # 'Create/rebuild topology).') % map)
- # if not map and ret != 0:
- # raise gcmd.GException(_('Unable to open vector map <%s> for editing.\n\n'
- # 'Data are probably corrupted, '
- # 'try to run v.build to rebuild '
- # 'the topology (Vector->Develop vector map->'
- # 'Create/rebuild topology).') % map)
-
- self.InitCats()
- def CloseMap(self):
- """!Close currently open vector map
- """
- if not self.map:
- return
-
- self._display.CloseMap()
- def InitCats(self):
- """!Initialize categories information
-
- @return 0 on success
- @return -1 on error
- """
- self.cats.clear()
- mapInfo = self._display.mapInfo
- if not mapInfo:
- return -1
-
- ndblinks = Vect_get_num_dblinks(byref(mapInfo))
- for i in range(ndblinks):
- fi = Vect_get_dblink(byref(mapInfo), i).contents
- if fi:
- self.cats[fi.number] = None
-
- # find max category
- nfields = Vect_cidx_get_num_fields(byref(mapInfo))
- Debug.msg(2, "wxDigit.InitCats(): nfields=%d", nfields)
-
- for i in range(nfields):
- field = Vect_cidx_get_field_number(byref(mapInfo), i)
- ncats = Vect_cidx_get_num_cats_by_index(byref(mapInfo), i)
- if field <= 0:
- continue
- for j in range(ncats):
- cat = c_int()
- type = c_int()
- id = c_int()
- Vect_cidx_get_cat_by_index(byref(mapInfo), i, j,
- byref(cat), byref(type), byref(id))
- if self.cats.has_key(field):
- if cat > self.cats[field]:
- self.cats[field] = cat.value
- else:
- self.cats[field] = cat.value
- Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
-
- # set default values
- for field, cat in self.cats.iteritems():
- if cat == None:
- self.cats[field] = 0 # first category 1
- Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
-
- def AddLine(self):
- pass
- def RewriteLine(self):
- pass
-
- def SplitLine(self):
- pass
- def DeleteLines(self):
- pass
- def MoveLines(self):
- pass
- def FlipLines(self):
- pass
- def MergeLines(self):
- pass
- def BreakLines(self):
- pass
- def SnapLines(self):
- pass
- def ConnectLines(self):
- pass
- def TypeConvLines(self):
- pass
- def ZBulkLabeling(self):
- pass
- def CopyLines(self):
- pass
- def MoveVertex(self):
- pass
- def ModifyLineVertex(self):
- pass
- def SelectLinesByQuery(self):
- pass
- def GetLineLength(self):
- pass
- def GetAreaSize(self):
- pass
- def GetAreaPerimeter(self):
- pass
- def CopyCats(self):
- pass
- def GetCategory(self, layer):
- """!Get max category number for layer
-
- @param layer layer number
-
- @return category number (0 if no category found)
- @return -1 on error
- """
- if cats.find(layer) != cats.end():
- Debug.msg(3, "vdigit.GetCategory(): layer=%d, cat=%d", layer, cats[layer])
- return cats[layer]
-
- return 0
-
- def GetLineCats(self):
- pass
- def SetLineCats(self):
- pass
-
- def GetLayers(self):
- pass
-
- def Undo(self):
- pass
- def GetUndoLevel(self):
- pass
- def UpdateSettings(self, breakLines, addCentroid, catBoundary):
- """!Update digit settings
-
- @param breakLines break lines on intersection
- @param addCentroid add centroid to left/right area
- @param catBoundary attach category to boundary
- """
- self._settings['breakLines'] = breakLines
- self._settings['addCentroid'] = addCentroid
- self._settings['catBoundary'] = None # !catBoundary # do not attach
- def SetCategory(self):
- """!Return category number to use (according Settings)"""
- if not UserSettings.Get(group = 'vdigit', key = 'categoryMode', subkey = 'selection'):
- self.SetCategoryNextToUse()
-
- return UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value')
- def SetCategoryNextToUse(self):
- """!Find maximum category number in the map layer
- and update Digit.settings['category']
- @return 'True' on success, 'False' on failure
- """
- # vector map layer without categories, reset to '1'
- UserSettings.Set(group = 'vdigit', key = 'category', subkey = 'value', value = 1)
-
- if self.map:
- cat = self.GetCategory(UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value'))
- cat += 1
- UserSettings.Set(group = 'vdigit', key = 'category', subkey = 'value',
- value = cat)
|