GRASS Programmer's Manual
6.4.2(2012)
|
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