GRASS Programmer's Manual  6.4.2(2012)
wxvdigit.py
Go to the documentation of this file.
00001 """!
00002 @package wxvdigit.py
00003 
00004 @brief wxGUI vector digitizer (base class)
00005 
00006 Code based on wxVdigit C++ component from GRASS 6.4.0
00007 (gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
00008 
00009 List of classes:
00010  - VDigitError
00011  - IVDigit
00012 
00013 (C) 2007-2011 by the GRASS Development Team
00014 
00015 This program is free software under the GNU General Public License
00016 (>=v2). Read the file COPYING that comes with GRASS for details.
00017 
00018 @author Martin Landa <landa.martin gmail.com>
00019 """
00020 
00021 from gcmd        import GError
00022 from debug       import Debug
00023 from preferences import globalSettings as UserSettings
00024 
00025 from wxvdriver   import DisplayDriver
00026 
00027 from grass.lib.gis    import *
00028 from grass.lib.vector import *
00029 from grass.lib.vedit  import *
00030 from grass.lib.dbmi   import *
00031 
00032 class VDigitError:
00033     def __init__(self, parent):
00034         """!Class for managing error messages of vector digitizer
00035 
00036         @param parent parent window for dialogs
00037         """
00038         self.parent  = parent
00039         self.caption = _('Digitization Error')
00040     
00041     def NoMap(self, name = None):
00042         """!No map for editing"""
00043         if name:
00044             message = _('Unable to open vector map <%s>.') % name
00045         else:
00046             message =  _('No vector map open for editing.')
00047         GError(message + ' ' + _('Operation cancelled.'),
00048                parent  = self.parent,
00049                caption = self.caption)
00050 
00051     def WriteLine(self):
00052         """!Writing line failed
00053         """
00054         GError(message = _('Writing new feature failed. '
00055                            'Operation cancelled.'),
00056                parent  = self.parent,
00057                caption = self.caption)
00058 
00059     def ReadLine(self, line):
00060         """!Reading line failed
00061         """
00062         GError(message = _('Reading feature id %d failed. '
00063                            'Operation cancelled.') % line,
00064                parent  = self.parent,
00065                caption = self.caption)
00066 
00067     def DbLink(self, dblink):
00068         """!No dblink available
00069         """
00070         GError(message = _('Database link %d not available. '
00071                            'Operation cancelled.') % dblink,
00072                parent  = self.parent,
00073                caption = self.caption)
00074 
00075     def Driver(self, driver):
00076         """!Staring driver failed
00077         """
00078         GError(message = _('Unable to start database driver <%s>. '
00079                            'Operation cancelled.') % driver,
00080                parent  = self.parent,
00081                caption = self.caption)
00082 
00083     def Database(self, driver, database):
00084         """!Opening database failed
00085         """
00086         GError(message = _('Unable to open database <%(db)s> by driver <%(driver)s>. '
00087                            'Operation cancelled.') % { 'db' : database, 'driver' : driver},
00088                parent  = self.parent,
00089                caption = self.caption)
00090 
00091     def DbExecute(self, sql):
00092         """!Sql query failed
00093         """
00094         GError(message = _("Unable to execute SQL query '%s'. "
00095                            "Operation cancelled.") % sql,
00096                parent  = self.parent,
00097                caption = self.caption)
00098 
00099     def DeadLine(self, line):
00100         """!Dead line
00101         """
00102         GError(message = _("Feature id %d is marked as dead. "
00103                            "Operation cancelled.") % line,
00104                parent  = self.parent,
00105                caption = self.caption)
00106 
00107     def FeatureType(self, ftype):
00108         """!Unknown feature type
00109         """
00110         GError(message = _("Unsupported feature type %d. "
00111                            "Operation cancelled.") % ftype,
00112                parent  = self.parent,
00113                caption = self.caption)
00114         
00115 class IVDigit:
00116     def __init__(self, mapwindow):
00117         """!Base class for vector digitizer (ctypes interface)
00118         
00119         @parem mapwindow reference for map window (BufferedWindow)
00120         """
00121         self.poMapInfo   = None      # pointer to Map_info
00122         self.mapWindow = mapwindow
00123 
00124         # background map
00125         self.bgMapInfo   = Map_info()
00126         self.poBgMapInfo = self.popoBgMapInfo = None
00127         
00128         if not mapwindow.parent.IsStandalone():
00129             goutput = mapwindow.parent.GetLayerManager().GetLogWindow()
00130             log = goutput.GetLog(err = True)
00131             progress = goutput.GetProgressBar()
00132         else:
00133             log = sys.stderr
00134             progress = None
00135         
00136         self.toolbar = mapwindow.parent.toolbars['vdigit']
00137         
00138         self._error   = VDigitError(parent = self.mapWindow)
00139         
00140         self._display = DisplayDriver(device    = mapwindow.pdcVector,
00141                                       deviceTmp = mapwindow.pdcTmp,
00142                                       mapObj    = mapwindow.Map,
00143                                       window    = mapwindow,
00144                                       glog      = log,
00145                                       gprogress = progress)
00146         
00147         # GRASS lib
00148         self.poPoints = Vect_new_line_struct()
00149         self.poCats   = Vect_new_cats_struct()
00150         
00151         # self.SetCategory()
00152         
00153         # layer / max category
00154         self.cats = dict()
00155         
00156         self._settings = dict()
00157         self.UpdateSettings() # -> self._settings
00158         
00159         # undo/redo
00160         self.changesets = dict()
00161         self.changesetCurrent = -1 # first changeset to apply
00162         self.changesetEnd     = -1 # last changeset to be applied
00163         
00164         if self.poMapInfo:
00165             self.InitCats()
00166         
00167     def __del__(self):
00168         Debug.msg(1, "IVDigit.__del__()")
00169         Vect_destroy_line_struct(self.poPoints)
00170         self.poPoints = None
00171         Vect_destroy_cats_struct(self.poCats)
00172         self.poCats = None
00173         
00174         if self.poBgMapInfo:
00175             Vect_close(self.poBgMapInfo)
00176             self.poBgMapInfo = self.popoBgMapInfo = None
00177             del self.bgMapInfo
00178         
00179     def CloseBackgroundMap(self):
00180         """!Close background vector map"""
00181         if not self.poBgMapInfo:
00182             return
00183         
00184         Vect_close(self.poBgMapInfo)
00185         self.poBgMapInfo = self.popoBgMapInfo = None
00186         
00187     def OpenBackgroundMap(self, bgmap):
00188         """!Open background vector map
00189 
00190         @todo support more background maps then only one
00191         
00192         @param bgmap name of vector map to be opened
00193 
00194         @return pointer to map_info
00195         @return None on error
00196         """
00197         name   = create_string_buffer(GNAME_MAX)
00198         mapset = create_string_buffer(GMAPSET_MAX)
00199         if not G__name_is_fully_qualified(bgmap, name, mapset):
00200             name   = str(bgmap)
00201             mapset = str(G_find_vector2(bgmap, ''))
00202         else:
00203             name   = str(name.value)
00204             mapset = str(mapset.value)
00205         
00206         if (name == Vect_get_name(self.poMapInfo) and \
00207                 mapset == Vect_get_mapset(self.poMapInfo)):
00208             self.poBgMapInfo = self.popoBgMapInfo = None
00209             self._error.NoMap(bgmap)
00210             return
00211         
00212         self.poBgMapInfo = pointer(self.bgMapInfo)
00213         self.popoBgMapInfo = pointer(self.poBgMapInfo)
00214         if Vect_open_old(self.poBgMapInfo, name, mapset) == -1:
00215             self.poBgMapInfo = self.popoBgMapInfo = None
00216             self._error.NoMap(bgmap)
00217             return
00218         
00219     def _getSnapMode(self):
00220         """!Get snapping mode
00221 
00222          - snap to vertex
00223          - snap to nodes
00224          - no snapping
00225         
00226         @return snap mode
00227         """
00228         threshold = self._display.GetThreshold()
00229         if threshold > 0.0:
00230             if UserSettings.Get(group = 'vdigit', key = 'snapToVertex', subkey = 'enabled'):
00231                 return SNAPVERTEX
00232             else:
00233                 return SNAP
00234         else:
00235             return NO_SNAP
00236     
00237     def _breakLineAtIntersection(self, line, pointsLine, changeset):
00238         """!Break given line at intersection
00239 
00240         \param line line id
00241         \param pointsLine line geometry
00242         \param changeset id
00243   
00244         \return number of modified lines
00245         """
00246         if not self._checkMap():
00247             return -1
00248         
00249         if not Vect_line_alive(self.poMapInfo, line):
00250             return 0
00251         
00252         if not pointsLine:
00253             if Vect_read_line(self.poMapInfo, self.poPoints, None, line) < 0:
00254                 self._error.ReadLine(line)
00255                 return -1
00256             points = self.poPoints
00257         else:
00258             points = pointsLine
00259         
00260         listLine  = Vect_new_list()
00261         listRef   = Vect_new_list()
00262         listBreak = Vect_new_list()
00263     
00264         pointsCheck = Vect_new_line_struct()
00265     
00266         lineBox = bound_box()
00267         # find all relevant lines
00268         Vect_get_line_box(self.poMapInfo, line, byref(lineBox))
00269         Vect_select_lines_by_box(self.poMapInfo, byref(lineBox),
00270                                  GV_LINES, listLine)
00271     
00272         # check for intersection
00273         Vect_list_append(listBreak, line)
00274         Vect_list_append(listRef, line)
00275         for i in range(listLine.contents.n_values):
00276             lineBreak = listLine.contents.value[i]
00277             if lineBreak == line:
00278                 continue
00279             
00280             ltype = Vect_read_line(self.poMapInfo, pointsCheck, None, lineBreak)
00281             if not (ltype & GV_LINES):
00282                 continue
00283             
00284             if Vect_line_check_intersection(self.poPoints, pointsCheck,
00285                                             WITHOUT_Z):
00286                 Vect_list_append(listBreak, lineBreak)
00287         
00288         nlines = Vect_get_num_lines(self.poMapInfo)
00289         
00290         for i in range(listBreak.contents.n_values):
00291             self._addActionToChangeset(changeset, listBreak.contents.value[i], add = False)
00292         
00293         ret = Vect_break_lines_list(self.poMapInfo, listBreak, listRef,
00294                                     GV_LINES, None)
00295         
00296         for i in range(listBreak.contents.n_values):
00297             if Vect_line_alive(self.poMapInfo, listBreak.contents.value[i]):
00298                 self._removeActionFromChangeset(changeset, listBreak.contents.value[i],
00299                                                 add = False)
00300         
00301         for line in range(nlines + 1, Vect_get_num_lines(self.poMapInfo) + 1):
00302             self._addActionToChangeset(changeset, line, add = True)
00303         
00304         Vect_destroy_line_struct(pointsCheck)
00305 
00306         Vect_destroy_list(listLine)
00307         Vect_destroy_list(listBreak)
00308         Vect_destroy_list(listRef)
00309         
00310         return ret
00311     
00312     def _addActionsBefore(self):
00313         """!Register action before operation
00314   
00315         @return changeset id
00316         """
00317         changeset = len(self.changesets)
00318         for line in self._display.selected['ids']:
00319             if Vect_line_alive(self.poMapInfo, line):
00320                 self._addActionToChangeset(changeset, line, add = False)
00321         
00322         return changeset
00323 
00324     def _applyChangeset(self, changeset, undo):
00325         """!Apply changeset (undo/redo changeset)
00326         
00327         @param changeset changeset id
00328         @param undo True for undo otherwise redo
00329 
00330         @return 1 changeset applied
00331         @return 0 changeset not applied
00332         @return -1 on error
00333         """
00334         if changeset < 0 or changeset > len(self.changesets.keys()):
00335             return -1
00336         
00337         if self.changesetEnd < 0:
00338             self.changesetEnd = changeset
00339             
00340         ret = 0
00341         actions = self.changesets[changeset]
00342         for action in actions: 
00343             add = action['add']
00344             line = action['line']
00345             if (undo and add) or \
00346                     (not undo and not add):
00347                 if Vect_line_alive(self.poMapInfo, line):
00348                     Debug.msg(3, "IVDigit._applyChangeset(): changeset=%d, action=add, line=%d -> deleted",
00349                               changeset, line)
00350                     Vect_delete_line(self.poMapInfo, line)
00351                     ret = 1
00352                 else:
00353                     Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=add, line=%d dead",
00354                               changeset, line)
00355             else: # delete
00356                 offset = action['offset']
00357                 if not Vect_line_alive(self.poMapInfo, line):
00358                     Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d -> added",
00359                               changeset, line)
00360                     if Vect_restore_line(self.poMapInfo, line, offset) < 0:
00361                         return -1
00362                     ret = 1
00363                 else:
00364                     Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d alive",
00365                               changeset, line)
00366         
00367         return ret
00368     
00369     def _addActionsAfter(self, changeset, nlines):
00370         """!Register action after operation
00371 
00372         @param changeset changeset id
00373         @param nline number of lines
00374         """
00375         for line in self._display.selected['ids']:
00376             if Vect_line_alive(self.poMapInfo, line):
00377                 self._removeActionFromChangeset(changeset, line, add = False)
00378         
00379         for line in range(nlines + 1, Vect_get_num_lines(self.poMapInfo)):
00380             if Vect_line_alive(self.poMapInfo, line):
00381                 self._addActionToChangeset(changeset, line, add = True)
00382         
00383     def _addActionToChangeset(self, changeset, line, add):
00384         """!Add action to changeset
00385         
00386         @param changeset id of changeset
00387         @param line feature id
00388         @param add True to add, otherwise delete
00389         """
00390         if not self._checkMap():
00391             return 
00392         
00393         if not Vect_line_alive(self.poMapInfo, line):
00394             return
00395         
00396         offset = Vect_get_line_offset(self.poMapInfo, line)
00397         
00398         if changeset not in self.changesets:
00399             self.changesets[changeset] = list()
00400             self.changesetCurrent = changeset
00401         
00402         self.changesets[changeset].append({ 'add'    : add,
00403                                             'line'   : line,
00404                                             'offset' : offset })
00405         
00406         Debug.msg(3, "IVDigit._addActionToChangeset(): changeset=%d, add=%d, line=%d, offset=%d",
00407                   changeset, add, line, offset)
00408         
00409     def _removeActionFromChangeset(self, changeset, line, add):
00410         """!Remove action from changeset
00411         
00412         @param changeset changeset id
00413         @param line line id
00414         @param add True for add, False for delete
00415         
00416         @return number of actions in changeset
00417         @return -1 on error
00418         """
00419         if changeset not in self.changesets.keys():
00420             return -1
00421         
00422         alist = self.changesets[changeset] 
00423         for action in alist:
00424             if action['add'] == add and action['line'] == line:
00425                 alist.remove(action)
00426         
00427         return len(alist)
00428 
00429     def AddFeature(self, ftype, points):
00430         """!Add new feature
00431         
00432         @param ftype feature type (point, line, centroid, boundary)
00433         @param points tuple of points ((x, y), (x, y), ...)
00434         
00435         @return tuple (number of added features, feature ids)
00436         """
00437         if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
00438             layer = -1 # -> no category
00439             cat   = -1
00440         else:
00441             layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
00442             cat   = self.SetCategory()
00443         
00444         if ftype == 'point':
00445             vtype = GV_POINT
00446         elif ftype == 'line':
00447             vtype = GV_LINE
00448         elif ftype == 'centroid':
00449             vtype = GV_CENTROID
00450         elif ftype == 'boundary':
00451             vtype = GV_BOUNDARY
00452         elif ftype == 'area':
00453             vtype = GV_AREA
00454         else:
00455             GError(parent = self.mapWindow,
00456                    message = _("Unknown feature type '%s'") % ftype)
00457             return (-1, None)
00458         
00459         if vtype & GV_LINES and len(points) < 2:
00460             GError(parent = self.mapWindow,
00461                    message = _("Not enough points for line"))
00462             return (-1, None)
00463         
00464         self.toolbar.EnableUndo()
00465         
00466         return self._addFeature(vtype, points, layer, cat,
00467                                 self._getSnapMode(), self._display.GetThreshold())
00468     
00469     def DeleteSelectedLines(self):
00470         """!Delete selected features
00471 
00472         @return number of deleted features
00473         """
00474         deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
00475         if not self._checkMap():
00476             return -1
00477         
00478         n_dblinks = Vect_get_num_dblinks(self.poMapInfo)
00479         Cats_del = None
00480         
00481         # collect categories for delete if requested
00482         if deleteRec:
00483             poCats    = Vect_new_cats_struct()
00484             poCatsDel = Vect_new_cats_struct()
00485             for i in self._display.selected['ids']:
00486                 if Vect_read_line(self.poMapInfo, None, poCats, i) < 0:
00487                     Vect_destroy_cats_struct(poCatsDel)
00488                     self._error.ReadLine(i)
00489                     return -1
00490                 
00491                 cats = poCats.contents
00492                 for j in range(cats.n_cats):
00493                     Vect_cat_set(poCatsDel, cats.field[j], cats.cat[j])
00494             
00495             Vect_destroy_cats_struct(poCats)
00496         
00497         # register changeset
00498         changeset = self._addActionsBefore()
00499         
00500         poList = self._display.GetSelectedIList()
00501         nlines = Vedit_delete_lines(self.poMapInfo, poList)
00502         Vect_destroy_list(poList)
00503         self._display.selected['ids'] = list()
00504         
00505         if nlines > 0 and deleteRec:
00506             handle  = dbHandle()
00507             poHandle = pointer(handle)
00508             stmt    = dbString()
00509             poStmt   = pointer(stmt)
00510             
00511             for dblink in range(n_dblinks):
00512                 poFi = Vect_get_dblink(self.poMapInfo, dblink)
00513                 if poFi is None:
00514                     self._error.DbLink(dblink)
00515                     return -1
00516                 
00517                 Fi = poFi.contents
00518                 poDriver = db_start_driver(Fi.driver)
00519                 if poDriver is None:
00520                     self._error.Driver(Fi.driver)
00521                     return -1
00522                 
00523                 db_init_handle(poHandle)
00524                 db_set_handle(poHandle, Fi.database, None)
00525                 if db_open_database(poDriver, poHandle) != DB_OK:
00526                     self._error.Database(Fi.driver, Fi.database)
00527                     return -1
00528                 
00529                 db_init_string(poStmt)
00530                 db_set_string(poStmt, "DELETE FROM %s WHERE" % Fi.table)
00531                 n_cats = 0;
00532                 catsDel = poCatsDel.contents
00533                 for c in range(catsDel.n_cats):
00534                     if catsDel.field[c] == Fi.number:
00535                         if n_cats > 0:
00536                             db_append_string(poStmt, " or")
00537                     
00538                     db_append_string(poStmt, " %s = %d" % (Fi.key, catsDel.cat[c]))
00539                     n_cats += 1
00540                 
00541                 Vect_cat_del(poCatsDel, Fi.number)
00542                 
00543                 if n_cats and \
00544                         db_execute_immediate(poDriver, poStmt) != DB_OK:
00545                     self._error.DbExecute(db_get_string(poStmt))
00546                     return -1
00547                 
00548                 db_close_database(poDriver)
00549                 db_shutdown_driver(poDriver)
00550         
00551         if poCatsDel:
00552             Vect_destroy_cats_struct(poCatsDel)
00553         
00554         if nlines > 0:
00555             self.toolbar.EnableUndo()
00556         
00557         return nlines
00558 
00559     def MoveSelectedLines(self, move):
00560         """!Move selected features
00561 
00562         @param move direction (x, y)
00563         """
00564         if not self._checkMap():
00565             return -1
00566         
00567         thresh = self._display.GetThreshold()
00568         snap   = self._getSnapMode()
00569         
00570         nlines = Vect_get_num_lines(self.poMapInfo)
00571         
00572         # register changeset
00573         changeset = self._addActionsBefore()
00574         
00575         poList = self._display.GetSelectedIList()
00576         nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
00577                                   poList,
00578                                   move[0], move[1], 0,
00579                                   snap, thresh)
00580         Vect_destroy_list(poList)
00581         
00582         if nlines > 0:
00583             self._addActionsAfter(changeset, nlines)
00584         else:
00585             del self.changesets[changeset]
00586         
00587         if nlines > 0 and self._settings['breakLines']:
00588             for i in range(1, nlines):
00589                 self._breakLineAtIntersection(nlines + i, None, changeset)
00590         
00591         if nlines > 0:
00592             self.toolbar.EnableUndo()
00593         
00594         return nlines
00595 
00596     def MoveSelectedVertex(self, point, move):
00597         """!Move selected vertex of the line
00598 
00599         @param point location point
00600         @param move  x,y direction
00601         
00602         @return id of new feature
00603         @return 0 vertex not moved (not found, line is not selected)
00604         @return -1 on error
00605         """
00606         if not self._checkMap():
00607             return -1
00608         
00609         if len(self._display.selected['ids']) != 1:
00610             return -1
00611         
00612         Vect_reset_line(self.poPoints)
00613         Vect_append_point(self.poPoints, point[0], point[1], 0.0)
00614         
00615         nlines = Vect_get_num_lines(self.poMapInfo)
00616         
00617         changeset = self._addActionsBefore()
00618         
00619         # move only first found vertex in bbox 
00620         poList = self._display.GetSelectedIList()
00621         moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
00622                                   poList, self.poPoints,
00623                                   self._display.GetThreshold(type = 'selectThresh'),
00624                                   self._display.GetThreshold(),
00625                                   move[0], move[1], 0.0,
00626                                   1, self._getSnapMode())
00627         Vect_destroy_list(poList)
00628         
00629         if moved > 0:
00630             self._addActionsAfter(changeset, nlines)
00631         else:
00632             del self.changesets[changeset]
00633         
00634         if moved > 0 and self._settings['breakLines']:
00635             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
00636                                           None, changeset)
00637         
00638         if moved > 0:
00639             self.toolbar.EnableUndo()
00640         
00641         return moved
00642 
00643     def AddVertex(self, coords):
00644         """!Add new vertex to the selected line/boundary on position 'coords'
00645 
00646         @param coords coordinates to add vertex
00647 
00648         @return id of new feature
00649         @return 0 nothing changed
00650         @return -1 on failure
00651         """
00652         added = self._ModifyLineVertex(coords, add = True)
00653         
00654         if added > 0:
00655             self.toolbar.EnableUndo()
00656 
00657         return added
00658 
00659     def RemoveVertex(self, coords):
00660         """!Remove vertex from the selected line/boundary on position 'coords'
00661 
00662         @param coords coordinates to remove vertex
00663 
00664         @return id of new feature
00665         @return 0 nothing changed
00666         @return -1 on failure
00667         """
00668         deleted = self._ModifyLineVertex(coords, add = False)
00669         
00670         if deleted > 0:
00671             self.toolbar.EnableUndo()
00672 
00673         return deleted
00674 
00675 
00676     def SplitLine(self, point):
00677         """!Split/break selected line/boundary on given position
00678 
00679         @param point point where to split line
00680         
00681         @return 1 line modified
00682         @return 0 nothing changed
00683         @return -1 error
00684         """
00685         thresh = self._display.GetThreshold('selectThresh')
00686         if not self._checkMap():
00687             return -1
00688         
00689         poList  = self._display.GetSelectedIList()
00690         Vect_reset_line(self.poPoints)
00691         Vect_append_point(self.poPoints, point[0], point[1], 0.0)
00692         
00693         nlines = Vect_get_num_lines(self.poMapInfo)
00694         
00695         changeset = self._addActionsBefore()
00696         
00697         ret = Vedit_split_lines(self.poMapInfo, poList,
00698                                 self.poPoints, thresh, None)
00699         Vect_destroy_list(poList)
00700         
00701         if ret > 0:
00702             self._addActionsAfter(changeset, nlines)
00703             self.toolbar.EnableUndo()
00704         else:
00705             del self.changesets[changeset]
00706         
00707         return ret
00708 
00709     def EditLine(self, line, coords):
00710         """!Edit existing line/boundary
00711 
00712         @param line feature id to be modified
00713         @param coords list of coordinates of modified line
00714 
00715         @return feature id of new line
00716         @return -1 on error
00717         """
00718         if not self._checkMap():
00719             return -1
00720         
00721         if len(coords) < 2:
00722             self.DeleteSelectedLines()
00723             return 0
00724         
00725         if not Vect_line_alive(self.poMapInfo, line):
00726             self._error.DeadLine(line)
00727             return -1
00728         
00729         # read original feature
00730         ltype = Vect_read_line(self.poMapInfo, None, self.poCats, line)
00731         if ltype < 0:
00732             self._error.ReadLine(line)
00733             return -1
00734         
00735         # build feature geometry
00736         Vect_reset_line(self.poPoints)
00737         for p in coords:
00738             Vect_append_point(self.poPoints, p[0], p[1], 0.0)
00739 
00740         # apply snapping (node or vertex)
00741         snap = self._getSnapMode()
00742         if snap != NO_SNAP:
00743             modeSnap = not (snap == SNAP)
00744             Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo,
00745                             int(self.poBgMapInfo is not None),
00746                            -1, self.poPoints, self._display.GetThreshold(), modeSnap)
00747 
00748         nlines = Vect_get_num_lines(self.poMapInfo)
00749         
00750         changeset = self._addActionsBefore()
00751         newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
00752                                     self.poPoints, self.poCats)
00753         if newline > 0:
00754             self._addActionsAfter(changeset, nlines)
00755             self.toolbar.EnableUndo()
00756         else:
00757             del self.changesets[changeset]
00758         
00759         if newline > 0 and self._settings['breakLines']:
00760             self._breakLineAtIntersection(newline, None, changeset)
00761         
00762         return newline
00763 
00764     def FlipLine(self):
00765         """!Flip selected lines/boundaries
00766 
00767         @return number of modified lines
00768         @return -1 on error
00769         """
00770         if not self._checkMap():
00771             return -1
00772         
00773         nlines = Vect_get_num_lines(self.poMapInfo)
00774         
00775         # register changeset
00776         changeset = self._addActionsBefore()
00777         
00778         poList = self._display.GetSelectedIList()
00779         ret = Vedit_flip_lines(self.poMapInfo, poList)
00780         Vect_destroy_list(poList)
00781         
00782         if ret > 0:
00783             self._addActionsAfter(changeset, nlines)
00784             self.toolbar.EnableUndo()
00785         else:
00786             del self.changesets[changeset]
00787         
00788         return ret
00789 
00790     def MergeLine(self):
00791         """!Merge selected lines/boundaries
00792 
00793         @return number of modified lines
00794         @return -1 on error
00795         """
00796         if not self._checkMap():
00797             return -1
00798         
00799         nlines = Vect_get_num_lines(self.poMapInfo)
00800         
00801         changeset = self._addActionsBefore()
00802         
00803         poList = self._display.GetSelectedIList()
00804         ret = Vedit_merge_lines(self.poMapInfo, poList)
00805         Vect_destroy_list(poList)
00806         
00807         if ret > 0:
00808             self._addActionsAfter(changeset, nlines)
00809             self.toolbar.EnableUndo()
00810         else:
00811             del self.changesets[changeset]
00812                 
00813         return ret
00814 
00815     def BreakLine(self):
00816         """!Break selected lines/boundaries
00817 
00818         @return number of modified lines
00819         @return -1 on error
00820         """
00821         if not self._checkMap():
00822             return -1
00823         
00824         nlines = Vect_get_num_lines(self.poMapInfo)
00825         
00826         changeset = self._addActionsBefore()
00827         
00828         poList = self._display.GetSelectedIList()
00829         ret = Vect_break_lines_list(self.poMapInfo, poList, None,
00830                                     GV_LINES, None)
00831         Vect_destroy_list(poList)
00832         
00833         if ret > 0:
00834             self._addActionsAfter(changeset, nlines)
00835             self.toolbar.EnableUndo()
00836         else:
00837             del self.changesets[changeset]
00838                 
00839         return ret
00840 
00841     def SnapLine(self):
00842         """!Snap selected lines/boundaries
00843 
00844         @return on success
00845         @return -1 on error
00846         """
00847         if not self._checkMap():
00848             return -1
00849         
00850         nlines = Vect_get_num_lines(self.poMapInfo)
00851         
00852         changeset = self._addActionsBefore()
00853         
00854         poList = self._display.GetSelectedIList()
00855         Vect_snap_lines_list(self.poMapInfo, poList,
00856                              self._display.GetThreshold(), None)
00857         Vect_destroy_list(poList)
00858         
00859         if nlines < Vect_get_num_lines(self.poMapInfo):
00860             self._addActionsAfter(changeset, nlines)
00861             self.toolbar.EnableUndo()
00862         else:
00863             del self.changesets[changeset]
00864         
00865     def ConnectLine(self):
00866         """!Connect selected lines/boundaries
00867 
00868         @return 1 lines connected
00869         @return 0 lines not connected
00870         @return -1 on error
00871         """
00872         if not self._checkMap():
00873             return -1
00874         
00875         nlines = Vect_get_num_lines(self.poMapInfo)
00876         
00877         # register changeset
00878         changeset = self._addActionsBefore()
00879         
00880         poList = self._display.GetSelectedIList()
00881         ret = Vedit_connect_lines(self.poMapInfo, poList,
00882                                   self._display.GetThreshold())
00883         Vect_destroy_list(poList)
00884         
00885         if ret > 0:
00886             self._addActionsAfter(changeset, nlines)
00887             self.toolbar.EnableUndo()
00888         else:
00889             del self.changesets[changeset]
00890         
00891         return ret
00892         
00893     def CopyLine(self, ids = []):
00894         """!Copy features from (background) vector map
00895 
00896         @param ids list of line ids to be copied
00897 
00898         @return number of copied features
00899         @return -1 on error
00900         """
00901         if not self._checkMap():
00902             return -1
00903         
00904         nlines = Vect_get_num_lines(self.poMapInfo)
00905         
00906         poList = self._display.GetSelectedIList(ids)
00907         ret = Vedit_copy_lines(self.poMapInfo, self.poBgMapInfo,
00908                                poList)
00909         Vect_destroy_list(poList)
00910         
00911         if ret > 0:
00912             changeset = len(self.changesets)
00913             for line in (range(nlines + 1, Vect_get_num_lines(self.poMapInfo))):
00914                 self._addActionToChangeset(changeset, line, add = True)
00915             self.toolbar.EnableUndo()
00916         else:
00917             del self.changesets[changeset]
00918 
00919         if ret > 0 and self.poBgMapInfo and self._settings['breakLines']:
00920             for i in range(1, ret):
00921                 self._breakLineAtIntersection(nlines + i, None, changeset)
00922         
00923         return ret
00924 
00925     def CopyCats(self, fromId, toId, copyAttrb = False):
00926         """!Copy given categories to objects with id listed in ids
00927 
00928         @param cats ids of 'from' feature
00929         @param ids  ids of 'to' feature(s)
00930 
00931         @return number of modified features
00932         @return -1 on error
00933         """
00934         if len(fromId) < 1 or len(toId) < 1:
00935             return 0
00936         
00937         poCatsFrom = self.poCats
00938         poCatsTo = Vect_new_cats_struct();
00939         
00940         nlines = 0
00941         
00942         for fline in fromId:
00943             if not Vect_line_alive(self.poMapInfo, fline):
00944                 continue
00945             
00946             if Vect_read_line(self.poMapInfo, None, poCatsFrom, fline) < 0:
00947                 self._error.ReadLine(fline)
00948                 return -1
00949             
00950             for tline in toId:
00951                 if not Vect_line_alive(self.poMapInfo, tline):
00952                     continue
00953                 
00954                 ltype = Vect_read_line(self.poMapInfo, self.poPoints, poCatsTo, tline)
00955                 if ltype < 0:
00956                     self._error.ReadLine(fline)
00957                     return -1
00958                 
00959                 catsFrom = poCatsFrom.contents
00960                 for i in range(catsFrom.n_cats):
00961                     if not copyAttrb:
00962                         # duplicate category
00963                         cat = catsFrom.cat[i]
00964                     else:
00965                         # duplicate attributes
00966                         cat = self.cats[catsFrom.field[i]] + 1
00967                         self.cats[catsFrom.field[i]] = cat
00968                         poFi = Vect_get_field(self.poMapInfo, catsFrom.field[i])
00969                         if not poFi:
00970                             self._error.DbLink(i)
00971                             return -1
00972                         
00973                         fi = poFi.contents
00974                         driver = db_start_driver(fi.driver)
00975                         if not driver:
00976                             self._error.Driver(fi.driver)
00977                             return -1
00978                         
00979                         handle = dbHandle()
00980                         db_init_handle(byref(handle))
00981                         db_set_handle(byref(handle), fi.database, None)
00982                         if db_open_database(driver, byref(handle)) != DB_OK:
00983                             db_shutdown_driver(driver)
00984                             self._error.Database(fi.driver, fi.database)
00985                             return -1
00986                         
00987                         stmt = dbString()
00988                         db_init_string(byref(stmt))
00989                         db_set_string(byref(stmt),
00990                                       "SELECT * FROM %s WHERE %s=%d" % (fi.table, fi.key,
00991                                                                         catsFrom.cat[i]))
00992                         
00993                         cursor = dbCursor()
00994                         if db_open_select_cursor(driver, byref(stmt), byref(cursor),
00995                                                  DB_SEQUENTIAL) != DB_OK:
00996                                 db_close_database_shutdown_driver(driver)
00997                                 return -1
00998                         
00999                         table = db_get_cursor_table(byref(cursor))
01000                         ncols = db_get_table_number_of_columns(table)
01001                         
01002                         sql = "INSERT INTO %s VALUES (" % fi.table
01003                         # fetch the data
01004                         more = c_int()
01005                         while True:
01006                             if db_fetch(byref(cursor), DB_NEXT, byref(more)) != DB_OK:
01007                                 db_close_database_shutdown_driver(driver)
01008                                 return -1
01009                             if not more.value:
01010                                 break
01011                             
01012                             value_string = dbString()
01013                             for col in range(ncols):
01014                                 if col > 0:
01015                                     sql += ","
01016                                     
01017                                 column = db_get_table_column(table, col)
01018                                 if db_get_column_name(column) == fi.key:
01019                                     sql += "%d" % cat
01020                                     continue
01021                                 
01022                                 value = db_get_column_value(column)
01023                                 db_convert_column_value_to_string(column, byref(value_string))
01024                                 if db_test_value_isnull(value):
01025                                     sql += "NULL"
01026                                 else:
01027                                     ctype = db_sqltype_to_Ctype(db_get_column_sqltype(column))
01028                                     if ctype != DB_C_TYPE_STRING:
01029                                         sql += db_get_string(byref(value_string))
01030                                     else:
01031                                         sql += "'%s'" % db_get_string(byref(value_string))
01032                         
01033                         sql += ")"
01034                         db_set_string(byref(stmt), sql)
01035                         if db_execute_immediate(driver, byref(stmt)) != DB_OK:
01036                             db_close_database_shutdown_driver(driver)
01037                             return -1
01038                         
01039                         db_close_database_shutdown_driver(driver)
01040                         G_free(poFi)
01041                 
01042                 if Vect_cat_set(poCatsTo, catsFrom.field[i], cat) < 1:
01043                     continue
01044                 
01045                 if Vect_rewrite_line(self.poMapInfo, tline, ltype, self.poPoints, poCatsTo) < 0:
01046                     self._error.WriteLine()
01047                     return -1
01048                 
01049                 nlines +=1
01050         
01051         Vect_destroy_cats_struct(poCatsTo)
01052         
01053         if nlines > 0:
01054             self.toolbar.EnableUndo()
01055         
01056         return nlines
01057 
01058     def _selectLinesByQueryThresh(self):
01059         """!Generic method used for SelectLinesByQuery() -- to get
01060         threshold value"""
01061         thresh = 0.0
01062         if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
01063             thresh = UserSettings.Get(group = 'vdigit', key = 'queryLength', subkey = 'thresh')
01064             if UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'than-selection') == 0:
01065                 thresh = -1 * thresh
01066         else:
01067             thresh = UserSettings.Get(group = 'vdigit', key = 'queryDangle', subkey = 'thresh')
01068             if UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'than-selection') == 0:
01069                 thresh = -1 * thresh
01070         
01071         return thresh
01072 
01073     def SelectLinesByQuery(self, bbox):
01074         """!Select features by query
01075         
01076         @todo layer / 3D
01077         
01078         @param bbox bounding box definition
01079         """
01080         if not self._checkMap():
01081             return -1
01082         
01083         thresh = self._selectLinesByQueryThresh()
01084         
01085         query = QUERY_UNKNOWN
01086         if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
01087             query = QUERY_LENGTH
01088         else:
01089             query = QUERY_DANGLE
01090         
01091         ftype = GV_POINTS | GV_LINES # TODO: 3D
01092         layer = 1 # TODO
01093         
01094         ids = list()
01095         poList = Vect_new_list()
01096         coList = poList.contents
01097         if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'box'):
01098             Vect_reset_line(self.poPoints)
01099             x1, y1 = bbox[0]
01100             x2, y2 = bbox[1]
01101             z1 = z2 = 0.0
01102             
01103             Vect_append_point(self.poPoints, x1, y1, z1)
01104             Vect_append_point(self.poPoints, x2, y1, z2)
01105             Vect_append_point(self.poPoints, x2, y2, z1)
01106             Vect_append_point(self.poPoints, x1, y2, z2)
01107             Vect_append_point(self.poPoints, x1, y1, z1)
01108         
01109             Vect_select_lines_by_polygon(self.poMapInfo, self.poPoints, 0, None,
01110                                          ftype, poList)
01111             
01112             if coList.n_values == 0:
01113                 return ids
01114         
01115         Vedit_select_by_query(self.poMapInfo,
01116                               ftype, layer, thresh, query,
01117                               poList)
01118         
01119         for i in range(coList.n_values):
01120             ids.append(int(coList.value[i]))
01121             
01122         Debug.msg(3, "IVDigit.SelectLinesByQuery(): lines=%d", coList.n_values)    
01123         Vect_destroy_list(poList)
01124         
01125         return ids
01126 
01127     def IsVector3D(self):
01128         """!Check if open vector map is 3D
01129         """
01130         if not self._checkMap():
01131             return False
01132         
01133         return Vect_is_3d(self.poMapInfo)
01134     
01135     def GetLineLength(self, line):
01136         """!Get line length
01137 
01138         @param line feature id
01139 
01140         @return line length
01141         @return -1 on error
01142         """
01143         if not self._checkMap():
01144             return -1
01145         
01146         if not Vect_line_alive(self.poMapInfo, line):
01147             return -1
01148     
01149         ltype = Vect_read_line(self.poMapInfo, self.poPoints, None, line)
01150         if ltype < 0:
01151             self._error.ReadLine(line)
01152             return ret
01153         
01154         length = -1
01155         if ltype & GV_LINES: # lines & boundaries
01156             length = Vect_line_length(self.poPoints)
01157         
01158         return length
01159 
01160     def GetAreaSize(self, centroid):
01161         """!Get area size
01162 
01163         @param centroid centroid id
01164 
01165         @return area size
01166         @return -1 on error
01167         """
01168         if not self._checkMap():
01169             return -1
01170         
01171         ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
01172         if ltype < 0:
01173             self._error.ReadLine(line)
01174             return ret
01175         
01176         if ltype != GV_CENTROID:
01177             return -1
01178         
01179         area = Vect_get_centroid_area(self.poMapInfo, centroid)
01180         size = -1
01181         if area > 0:
01182             if not Vect_area_alive(self.poMapInfo, area):
01183                 return size
01184             
01185             size = Vect_get_area_area(self.poMapInfo, area)
01186         
01187         return size
01188         
01189     def GetAreaPerimeter(self, centroid):
01190         """!Get area perimeter
01191         
01192         @param centroid centroid id
01193         
01194         @return area size
01195         @return -1 on error
01196         """
01197         if not self._checkMap():
01198             return -1
01199         
01200         ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
01201         if ltype < 0:
01202             self._error.ReadLine(line)
01203             return ret
01204         
01205         if ltype != GV_CENTROID:
01206             return -1
01207         
01208         area = Vect_get_centroid_area(self.poMapInfo, centroid)
01209         perimeter = -1
01210         if area > 0:
01211             if not Vect_area_alive(self.poMapInfo, area):
01212                 return -1
01213             
01214             Vect_get_area_points(self.poMapInfo, area, self.poPoints)
01215             perimeter = Vect_area_perimeter(self.poPoints)
01216         
01217         return perimeter
01218     
01219     def SetLineCats(self, line, layer, cats, add = True):
01220         """!Set categories for given line and layer
01221 
01222         @param line feature id
01223         @param layer layer number (-1 for first selected line)
01224         @param cats list of categories
01225         @param add if True to add, otherwise do delete categories
01226 
01227         @return new feature id (feature need to be rewritten)
01228         @return -1 on error
01229         """
01230         if not self._checkMap():
01231             return -1
01232         
01233         if line < 1 and len(self._display.selected['ids']) < 1:
01234             return -1
01235         
01236         update = False
01237         if line == -1:
01238             update = True
01239             line = self._display.selected['ids'][0]
01240         
01241         if not Vect_line_alive(self.poMapInfo, line):
01242             return -1
01243         
01244         ltype = Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line)
01245         if ltype < 0:
01246             self._error.ReadLine(line)
01247             return -1
01248         
01249         for c in cats:
01250             if add:
01251                 Vect_cat_set(self.poCats, layer, c)
01252             else:
01253                 Vect_field_cat_del(self.poCats, layer, c)
01254         
01255         nlines = Vect_get_num_lines(self.poMapInfo)
01256         changeset = self._addActionsBefore()
01257         newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
01258                                     self.poPoints, self.poCats)
01259         
01260         if newline > 0:
01261             self._addActionsAfter(changeset, nlines)
01262             self.toolbar.EnableUndo()
01263         
01264         if update:
01265             # update line id since the line was rewritten
01266             self._display.selected['ids'][0] =  newline
01267         
01268         return newline
01269 
01270     def TypeConvForSelectedLines(self):
01271         """!Feature type conversion for selected objects.
01272 
01273         Supported conversions:
01274          - point <-> centroid
01275          - line <-> boundary
01276 
01277         @return number of modified features
01278         @return -1 on error
01279         """
01280         if not self._checkMap():
01281             return -1
01282         
01283         nlines = Vect_get_num_lines(self.poMapInfo)
01284         
01285         # register changeset
01286         changeset = self._addActionsBefore()
01287         
01288         poList = self._display.GetSelectedIList()
01289         ret = Vedit_chtype_lines(self.poMapInfo, poList)
01290         Vect_destroy_list(poList)
01291         
01292         if ret > 0:
01293             self._addActionsAfter(changeset, nlines)
01294             self.toolbar.EnableUndo()
01295         else:
01296             del self.changesets[changeset]
01297         
01298         return ret
01299 
01300     def Undo(self, level = -1):
01301         """!Undo action
01302 
01303         @param level levels to undo (0 to revert all)
01304 
01305         @return id of current changeset
01306         """
01307         changesetLast = len(self.changesets.keys()) - 1
01308 
01309         if changesetLast < 0:
01310             return changesetLast
01311         
01312         if self.changesetCurrent == -2: # value uninitialized 
01313             self.changesetCurrent = changesetLast
01314             
01315         if level > 0 and self.changesetCurrent < 0:
01316             self.changesetCurrent = 0
01317         
01318         if level == 0:
01319             # 0 -> undo all
01320             level = -1 * changesetLast + 1
01321 
01322         Debug.msg(2, "Digit.Undo(): changeset_last=%d, changeset_current=%d, level=%d",
01323                   changesetLast, self.changesetCurrent, level)
01324     
01325         if level < 0: # undo
01326             if self.changesetCurrent + level < -1:
01327                 return changesetCurrent;
01328             for changeset in range(self.changesetCurrent, self.changesetCurrent + level, -1):
01329                 self._applyChangeset(changeset, undo = True)
01330         elif level > 0: # redo 
01331             if self.changesetCurrent + level > len(self.changesets.keys()):
01332                 return self.changesetCurrent
01333             for changeset in range(self.changesetCurrent, self.changesetCurrent + level):
01334                 self._applyChangeset(changeset, undo = False)
01335         
01336         self.changesetCurrent += level
01337 
01338         Debug.msg(2, "Digit.Undo(): changeset_current=%d, changeset_last=%d, changeset_end=%d",
01339                   self.changesetCurrent, changesetLast, self.changesetEnd)
01340         
01341         if self.changesetCurrent == self.changesetEnd:
01342             self.changesetEnd = changesetLast
01343             return -1
01344         
01345         self.mapWindow.UpdateMap(render = False)
01346         
01347         if self.changesetCurrent < 0: # disable undo tool
01348             self.toolbar.EnableUndo(False)
01349 
01350     def ZBulkLines(self, pos1, pos2, start, step):
01351         """!Z-bulk labeling
01352 
01353         @param pos1 reference line (start point)
01354         @param pos1 reference line (end point)
01355         @param start starting value
01356         @param step step value
01357 
01358         @return number of modified lines
01359         @return -1 on error
01360         """
01361         if not self._checkMap():
01362             return -1
01363         
01364         nlines = Vect_get_num_lines(self.poMapInfo)
01365         
01366         # register changeset
01367         changeset = self._addActionsBefore()
01368         
01369         poList = self._display.GetSelectedIList()
01370         ret = Vedit_bulk_labeling(self.poMapInfo, poList,
01371                                   pos1[0], pos1[1], pos2[0], pos2[1],
01372                                   start, step)
01373         Vect_destroy_list(poList)
01374         
01375         if ret > 0:
01376             self._addActionsAfter(changeset, nlines)
01377             self.toolbar.EnableUndo()
01378         else:
01379             del self.changesets[changeset]
01380         
01381         return ret
01382     
01383     def GetDisplay(self):
01384         """!Get display driver instance"""
01385         return self._display
01386     
01387     def OpenMap(self, name):
01388         """!Open vector map for editing
01389         
01390         @param map name of vector map to be set up
01391         """
01392         Debug.msg (3, "AbstractDigit.SetMapName map=%s" % name)
01393         
01394         name, mapset = name.split('@')
01395         
01396         self.poMapInfo = self._display.OpenMap(str(name), str(mapset), True)
01397         
01398         if self.poMapInfo:
01399             self.InitCats()
01400         
01401         return self.poMapInfo
01402     
01403     def CloseMap(self):
01404         """!Close currently open vector map
01405         """
01406         if not self._checkMap():
01407             return
01408         
01409         self._display.CloseMap()
01410 
01411     def InitCats(self):
01412         """!Initialize categories information
01413         
01414         @return 0 on success
01415         @return -1 on error
01416         """
01417         self.cats.clear()
01418         if not self._checkMap():
01419             return -1
01420         
01421         ndblinks = Vect_get_num_dblinks(self.poMapInfo)
01422         for i in range(ndblinks):
01423             fi = Vect_get_dblink(self.poMapInfo, i).contents
01424             if fi:
01425                 self.cats[fi.number] = None
01426         
01427         # find max category
01428         nfields = Vect_cidx_get_num_fields(self.poMapInfo)
01429         Debug.msg(2, "wxDigit.InitCats(): nfields=%d", nfields)
01430         
01431         for i in range(nfields):
01432             field = Vect_cidx_get_field_number(self.poMapInfo, i)
01433             ncats = Vect_cidx_get_num_cats_by_index(self.poMapInfo, i)
01434             if field <= 0:
01435                 continue
01436             for j in range(ncats):
01437                 cat = c_int()
01438                 type = c_int()
01439                 id = c_int()
01440                 Vect_cidx_get_cat_by_index(self.poMapInfo, i, j,
01441                                            byref(cat), byref(type), byref(id))
01442                 if field in self.cats:
01443                     if cat > self.cats[field]:
01444                         self.cats[field] = cat.value
01445                 else:
01446                     self.cats[field] = cat.value
01447             Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
01448             
01449         # set default values
01450         for field, cat in self.cats.iteritems():
01451             if cat == None:
01452                 self.cats[field] = 0 # first category 1
01453             Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
01454         
01455     def _checkMap(self):
01456         """!Check if map is open
01457         """
01458         if not self.poMapInfo:
01459             self._error.NoMap()
01460             return False
01461         
01462         return True
01463 
01464     def _addFeature(self, ftype, coords, layer, cat, snap, threshold):
01465         """!Add new feature(s) to the vector map
01466 
01467         @param ftype feature type (GV_POINT, GV_LINE, GV_BOUNDARY, ...)
01468         @coords tuple of coordinates ((x, y), (x, y), ...)
01469         @param layer layer number (-1 for no cat)
01470         @param cat category number
01471         @param snap snap to node/vertex
01472         @param threshold threshold for snapping
01473         
01474         @return tuple (number of added features, list of fids)
01475         @return number of features -1 on error
01476         """
01477         fids = list()
01478         if not self._checkMap():
01479             return (-1, None)
01480         
01481         is3D = bool(Vect_is_3d(self.poMapInfo))
01482         
01483         Debug.msg(2, "IVDigit._addFeature(): npoints=%d, layer=%d, cat=%d, snap=%d",
01484                   len(coords), layer, cat, snap)
01485         
01486         if not (ftype & (GV_POINTS | GV_LINES | GV_AREA)): # TODO: 3D
01487             self._error.FeatureType(ftype)
01488             return (-1, None)
01489         
01490         # set category
01491         Vect_reset_cats(self.poCats)
01492         if layer > 0 and ftype != GV_AREA:
01493             Vect_cat_set(self.poCats, layer, cat)
01494             self.cats[layer] = max(cat, self.cats.get(layer, 1))
01495         
01496         # append points
01497         Vect_reset_line(self.poPoints)
01498         for c in coords:
01499             Vect_append_point(self.poPoints, c[0], c[1], 0.0)
01500         
01501         if ftype & (GV_BOUNDARY | GV_AREA):
01502             # close boundary
01503             points = self.poPoints.contents
01504             last = points.n_points - 1
01505             if Vect_points_distance(points.x[0], points.x[0], points.z[0],
01506                                     points.x[last], points.x[last], points.z[last],
01507                                     is3D) <= threshold:
01508                 points.x[last] = points.x[0]
01509                 points.y[last] = points.y[0]
01510                 points.z[last] = points.z[0]
01511         
01512         if snap != NO_SNAP:
01513             # apply snapping (node or vertex)
01514             modeSnap = not (snap == SNAP)
01515             Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
01516                             -1, self.poPoints, threshold, modeSnap)
01517         
01518         if ftype == GV_AREA:
01519             ltype = GV_BOUNDARY
01520         else:
01521             ltype = ftype
01522         newline = Vect_write_line(self.poMapInfo, ltype, self.poPoints, self.poCats)
01523         if newline < 0:
01524             self._error.WriteLine()
01525             return (-1, None)
01526         else:
01527             fids.append(newline)
01528         
01529         left = right = -1
01530         if ftype & GV_AREA:
01531             # add centroids for left/right area
01532             bpoints = Vect_new_line_struct()
01533             cleft = c_int()
01534             cright = c_int()
01535             
01536             Vect_get_line_areas(self.poMapInfo, newline,
01537                                 byref(cleft), byref(cright))
01538             left = cleft.value
01539             right = cright.value
01540             
01541             Debug.msg(3, "IVDigit._addFeature(): area - left=%d right=%d",
01542                       left, right)
01543             
01544             # check if area exists and has no centroid inside
01545             if layer > 0 and (left > 0 or right > 0):
01546                 Vect_cat_set(self.poCats, layer, cat)
01547                 self.cats[layer] = max(cat, self.cats.get(layer, 0))
01548             
01549             x = c_double()
01550             y = c_double()
01551             if left > 0 and \
01552                     Vect_get_area_centroid(self.poMapInfo, left) == 0:
01553                 # if Vect_get_area_points(self.poMapInfo, left, bpoints) > 0 and
01554                 # Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
01555                 if Vect_get_point_in_area(self.poMapInfo, left, byref(x), byref(y)) == 0:
01556                     Vect_reset_line(bpoints)
01557                     Vect_append_point(bpoints, x.value, y.value, 0.0)
01558                     newline = Vect_write_line(self.poMapInfo, GV_CENTROID,
01559                                               bpoints, self.poCats)
01560                     if newline < 0:
01561                         self._error.WriteLine()
01562                         return (len(fids), fids)
01563                     else:
01564                         fids.append(newline)
01565                     
01566             if right > 0 and \
01567                     Vect_get_area_centroid(self.poMapInfo, right) == 0:
01568                 # if Vect_get_area_points(byref(self.poMapInfo), right, bpoints) > 0 and 
01569                 # Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
01570                 if Vect_get_point_in_area(self.poMapInfo, right, byref(x), byref(y)) == 0:
01571                     Vect_reset_line(bpoints)
01572                     Vect_append_point(bpoints, x.value, y.value, 0.0)
01573                     newline =  Vect_write_line(self.poMapInfo, GV_CENTROID,
01574                                                bpoints, self.poCats)
01575                     if newline < 0:
01576                         self._error.WriteLine()
01577                         return (len(fids, fids))
01578                     else:
01579                         fids.append(newline)
01580                     
01581             Vect_destroy_line_struct(bpoints)
01582         
01583         # register changeset
01584         changeset = len(self.changesets)
01585         self._addActionToChangeset(changeset, newline, add = True)
01586         
01587         # break at intersection
01588         if self._settings['breakLines']:
01589             self._breakLineAtIntersection(newline, self.poPoints, changeset)
01590         
01591         return (len(fids), fids)
01592     
01593     def _ModifyLineVertex(self, coords, add = True):
01594         """!Add or remove vertex
01595         
01596         Shape of line/boundary is not changed when adding new vertex.
01597         
01598         @param coords coordinates of point
01599         @param add True to add, False to remove
01600         
01601         @return id id of the new feature
01602         @return 0 nothing changed
01603         @return -1 error
01604         """
01605         if not self._checkMap():
01606             return -1
01607         
01608         selected = self._display.selected
01609         if len(selected['ids']) != 1:
01610             return 0
01611         
01612         poList  = self._display.GetSelectedIList()
01613         Vect_reset_line(self.poPoints)
01614         Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
01615         
01616         nlines = Vect_get_num_lines(self.poMapInfo)
01617         thresh = self._display.GetThreshold(type = 'selectThresh')
01618         
01619         changeset = self._addActionsBefore()
01620         
01621         if add:
01622             ret = Vedit_add_vertex(self.poMapInfo, poList,
01623                                    self.poPoints, thresh)
01624         else:
01625             ret = Vedit_remove_vertex(self.poMapInfo, poList,
01626                                       self.poPoints, thresh)
01627         Vect_destroy_list(poList)
01628         
01629         if ret > 0:
01630             self._addActionsAfter(changeset, nlines)
01631         else:
01632             del self.changesets[changeset]
01633         
01634         if not add and ret > 0 and self._settings['breakLines']:
01635             self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
01636                                           None, changeset)
01637                 
01638         return nlines + 1 # feature is write at the end of the file
01639     
01640     def GetLineCats(self, line):
01641         """!Get list of layer/category(ies) for selected feature.
01642 
01643         @param line feature id (-1 for first selected feature)
01644 
01645         @return list of layer/cats
01646         """
01647         ret = dict()
01648         if not self._checkMap():
01649             return ret
01650         
01651         if line == -1 and len(self._display.selected['ids']) < 1:
01652             return ret
01653         
01654         if line == -1:
01655             line = self._display.selected['ids'][0]
01656             
01657         if not Vect_line_alive(self.poMapInfo, line):
01658             self._error.DeadLine(line)
01659             return ret
01660         
01661         if Vect_read_line(self.poMapInfo, None, self.poCats, line) < 0:
01662             self._error.ReadLine(line)
01663             return ret
01664         
01665         cats = self.poCats.contents
01666         for i in range(cats.n_cats):
01667             field = cats.field[i]
01668             if field not in ret:
01669                 ret[field] = list()
01670             ret[field].append(cats.cat[i])
01671         
01672         return ret
01673 
01674     def GetLayers(self):
01675         """!Get list of layers
01676         
01677         Requires self.InitCats() to be called.
01678 
01679         @return list of layers
01680         """
01681         return self.cats.keys()
01682     
01683     def UpdateSettings(self):
01684         """!Update digit (and display) settings
01685         """
01686         self._display.UpdateSettings()
01687         
01688         self._settings['breakLines']  = bool(UserSettings.Get(group = 'vdigit', key = "breakLines",
01689                                                               subkey = 'enabled'))
01690         
01691     def SetCategory(self):
01692         """!Update self.cats based on settings"""
01693         sel = UserSettings.Get(group = 'vdigit', key = 'categoryMode', subkey = 'selection')
01694         cat = None
01695         if sel == 0: # next to usep
01696             cat = self._setCategoryNextToUse()
01697         elif sel == 1:
01698             cat = UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value')
01699         
01700         if cat:
01701             layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
01702             self.cats[layer] = cat
01703         
01704         return cat
01705     
01706     def _setCategoryNextToUse(self):
01707         """!Find maximum category number for the given layer and
01708         update the settings
01709 
01710         @return category to be used
01711         """
01712         # get max category number for given layer and update the settings
01713         layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
01714         cat = self.cats.get(layer, 0) + 1
01715         UserSettings.Set(group = 'vdigit', key = 'category', subkey = 'value',
01716                          value = cat)
01717         Debug.msg(1, "IVDigit._setCategoryNextToUse(): cat=%d", cat)
01718         
01719         return cat
01720 
01721     def SelectLinesFromBackgroundMap(self, bbox):
01722         """!Select features from background map
01723 
01724         @param bbox bounding box definition
01725         
01726         @return list of selected feature ids
01727         """
01728         # try select features by box first
01729         if self._display.SelectLinesByBox(bbox, poMapInfo = self.poBgMapInfo) < 1:
01730             self._display.SelectLineByPoint(bbox[0], poMapInfo = self.poBgMapInfo)['line']
01731             
01732         return self._display.selected['ids']
01733         
01734     def GetUndoLevel(self):
01735         """!Get undo level (number of active changesets)
01736         
01737         Note: Changesets starts wiht 0
01738         """
01739         return self.changesetCurrent
01740     
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines