wxvdigit.py 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513
  1. """!
  2. @package wxvdigit.py
  3. @brief wxGUI vector digitizer (base class)
  4. Code based on wxVdigit C++ component from GRASS 6.4.0
  5. (gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
  6. List of classes:
  7. - VDigitError
  8. - IVDigit
  9. (C) 2007-2011 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Martin Landa <landa.martin gmail.com>
  13. """
  14. from gcmd import GError
  15. from debug import Debug
  16. from preferences import globalSettings as UserSettings
  17. from wxvdriver import DisplayDriver
  18. from grass.lib.grass import *
  19. from grass.lib.vector import *
  20. from grass.lib.vedit import *
  21. from grass.lib.dbmi import *
  22. class VDigitError:
  23. def __init__(self, parent):
  24. """!Class for managing error messages of vector digitizer
  25. @param parent parent window for dialogs
  26. """
  27. self.parent = parent
  28. self.caption = _('Digitization Error')
  29. def NoMap(self, name = None):
  30. """!No map for editing"""
  31. if name:
  32. message = _('Unable to open vector map <%s>.') % name
  33. else:
  34. message = _('No vector map open for editing.')
  35. GError(message + ' ' + _('Operation cancelled.'),
  36. parent = self.parent,
  37. caption = self.caption)
  38. def WriteLine(self):
  39. """!Writing line failed
  40. """
  41. GError(message = _('Writing new feature failed. '
  42. 'Operation cancelled.'),
  43. parent = self.parent,
  44. caption = self.caption)
  45. def ReadLine(self, line):
  46. """!Reading line failed
  47. """
  48. GError(message = _('Reading feature id %d failed. '
  49. 'Operation cancelled.') % line,
  50. parent = self.parent,
  51. caption = self.caption)
  52. def DbLink(self, dblink):
  53. """!No dblink available
  54. """
  55. GError(message = _('Database link %d not available. '
  56. 'Operation cancelled.') % dblink,
  57. parent = self.parent,
  58. caption = self.caption)
  59. def Driver(self, driver):
  60. """!Staring driver failed
  61. """
  62. GError(message = _('Unable to start database driver <%s>. '
  63. 'Operation cancelled.') % driver,
  64. parent = self.parent,
  65. caption = self.caption)
  66. def Database(self, driver, database):
  67. """!Opening database failed
  68. """
  69. GError(message = _('Unable to open database <%s> by driver <%s>. '
  70. 'Operation cancelled.') % (database, driver),
  71. parent = self.parent,
  72. caption = self.caption)
  73. def DbExecute(self, sql):
  74. """!Sql query failed
  75. """
  76. GError(message = _("Unable to execute SQL query '%s'. "
  77. "Operation cancelled.") % sql,
  78. parent = self.parent,
  79. caption = self.caption)
  80. def DeadLine(self, line):
  81. """!Dead line
  82. """
  83. GError(message = _("Feature id %d is marked as dead. "
  84. "Operation cancelled.") % line,
  85. parent = self.parent,
  86. caption = self.caption)
  87. def FeatureType(self, ftype):
  88. """!Unknown feature type
  89. """
  90. GError(message = _("Unsupported feature type %d. "
  91. "Operation cancelled.") % ftype,
  92. parent = self.parent,
  93. caption = self.caption)
  94. class IVDigit:
  95. def __init__(self, mapwindow):
  96. """!Base class for vector digitizer (ctypes interface)
  97. @parem mapwindow reference for map window (BufferedWindow)
  98. """
  99. self.poMapInfo = None # pointer to Map_info
  100. self.mapWindow = mapwindow
  101. # background map
  102. self.bgMapInfo = Map_info()
  103. self.poBgMapInfo = self.popoBgMapInfo = None
  104. if not mapwindow.parent.IsStandalone():
  105. goutput = mapwindow.parent.GetLayerManager().GetLogWindow()
  106. log = goutput.GetLog(err = True)
  107. progress = goutput.GetProgressBar()
  108. else:
  109. log = sys.stderr
  110. progress = None
  111. self.toolbar = mapwindow.parent.toolbars['vdigit']
  112. self._error = VDigitError(parent = self.mapWindow)
  113. self._display = DisplayDriver(device = mapwindow.pdcVector,
  114. deviceTmp = mapwindow.pdcTmp,
  115. mapObj = mapwindow.Map,
  116. window = mapwindow,
  117. glog = log,
  118. gprogress = progress)
  119. # GRASS lib
  120. self.poPoints = Vect_new_line_struct()
  121. self.poCats = Vect_new_cats_struct()
  122. # self.SetCategory()
  123. # layer / max category
  124. self.cats = dict()
  125. self._settings = dict()
  126. self.UpdateSettings() # -> self._settings
  127. # undo/redo
  128. self.changesets = dict()
  129. self.changesetCurrent = -1 # first changeset to apply
  130. self.changesetEnd = -1 # last changeset to be applied
  131. if self.poMapInfo:
  132. self.InitCats()
  133. def __del__(self):
  134. Vect_destroy_line_struct(self.poPoints)
  135. self.poPoints = None
  136. Vect_destroy_cats_struct(self.poCats)
  137. self.poCats = None
  138. if self.poBgMapInfo:
  139. Vect_close(self.poBgMapInfo)
  140. self.poBgMapInfo = self.popoBgMapInfo = None
  141. del self.bgMapInfo
  142. def CloseBackgroundMap(self):
  143. """!Close background vector map"""
  144. if not self.poBgMapInfo:
  145. return
  146. Vect_close(self.poBgMapInfo)
  147. self.poBgMapInfo = self.popoBgMapInfo = None
  148. def OpenBackgroundMap(self, bgmap):
  149. """!Open background vector map
  150. @todo support more background maps then only one
  151. @param bgmap name of vector map to be opened
  152. @return pointer to map_info
  153. @return None on error
  154. """
  155. name = create_string_buffer(GNAME_MAX)
  156. mapset = create_string_buffer(GMAPSET_MAX)
  157. if not G_name_is_fully_qualified(bgmap, name, mapset):
  158. name = str(bgmap)
  159. mapset = str(G_find_vector2(bgmap, ''))
  160. else:
  161. name = str(name.value)
  162. mapset = str(mapset.value)
  163. if (name == Vect_get_name(self.poMapInfo) and \
  164. mapset == Vect_get_mapset(self.poMapInfo)):
  165. self.poBgMapInfo = self.popoBgMapInfo = None
  166. self._error.NoMap(bgmap)
  167. return
  168. self.poBgMapInfo = pointer(self.bgMapInfo)
  169. self.popoBgMapInfo = pointer(self.poBgMapInfo)
  170. if Vect_open_old(self.poBgMapInfo, name, mapset) == -1:
  171. self.poBgMapInfo = self.popoBgMapInfo = None
  172. self._error.NoMap(bgmap)
  173. return
  174. def _getSnapMode(self):
  175. """!Get snapping mode
  176. - snap to vertex
  177. - snap to nodes
  178. - no snapping
  179. @return snap mode
  180. """
  181. threshold = self._display.GetThreshold()
  182. if threshold > 0.0:
  183. if UserSettings.Get(group = 'vdigit', key = 'snapToVertex', subkey = 'enabled'):
  184. return SNAPVERTEX
  185. else:
  186. return SNAP
  187. else:
  188. return NO_SNAP
  189. def _breakLineAtIntersection(self, line, pointsLine, changeset):
  190. """!Break given line at intersection
  191. \param line line id
  192. \param pointsLine line geometry
  193. \param changeset id
  194. \return number of modified lines
  195. """
  196. if not self._checkMap():
  197. return -1
  198. if not Vect_line_alive(self.poMapInfo, line):
  199. return 0
  200. if not pointsLine:
  201. if Vect_read_line(self.poMapInfo, self.poPoints, None, line) < 0:
  202. self._error.ReadLine(line)
  203. return -1
  204. points = self.poPoints
  205. else:
  206. points = pointsLine
  207. listLine = Vect_new_list()
  208. listRef = Vect_new_list()
  209. listBreak = Vect_new_list()
  210. pointsCheck = Vect_new_line_struct()
  211. lineBox = bound_box()
  212. # find all relevant lines
  213. Vect_get_line_box(self.poMapInfo, line, byref(lineBox))
  214. Vect_select_lines_by_box(self.poMapInfo, byref(lineBox),
  215. GV_LINES, listLine)
  216. # check for intersection
  217. Vect_list_append(listBreak, line)
  218. Vect_list_append(listRef, line)
  219. for i in range(listLine.contents.n_values):
  220. lineBreak = listLine.contents.value[i]
  221. if lineBreak == line:
  222. continue
  223. ltype = Vect_read_line(self.poMapInfo, pointsCheck, None, lineBreak)
  224. if not (ltype & GV_LINES):
  225. continue
  226. if Vect_line_check_intersection(self.poPoints, pointsCheck,
  227. WITHOUT_Z):
  228. Vect_list_append(listBreak, lineBreak)
  229. nlines = Vect_get_num_lines(self.poMapInfo)
  230. for i in range(listBreak.contents.n_values):
  231. self._addActionToChangeset(changeset, listBreak.contents.value[i], add = False)
  232. ret = Vect_break_lines_list(self.poMapInfo, listBreak, listRef,
  233. GV_LINES, None)
  234. for i in range(listBreak.contents.n_values):
  235. if Vect_line_alive(self.poMapInfo, listBreak.contents.value[i]):
  236. self._removeActionFromChangeset(changeset, listBreak.contents.value[i],
  237. add = False)
  238. for line in range(nlines + 1, Vect_get_num_lines(self.poMapInfo) + 1):
  239. self._addActionToChangeset(changeset, line, add = True)
  240. Vect_destroy_line_struct(pointsCheck)
  241. Vect_destroy_list(listLine)
  242. Vect_destroy_list(listBreak)
  243. Vect_destroy_list(listRef)
  244. return ret
  245. def _addActionsBefore(self):
  246. """!Register action before operation
  247. @return changeset id
  248. """
  249. changeset = len(self.changesets)
  250. for line in self._display.selected['ids']:
  251. if Vect_line_alive(self.poMapInfo, line):
  252. self._addActionToChangeset(changeset, line, add = False)
  253. return changeset
  254. def _applyChangeset(self, changeset, undo):
  255. """!Apply changeset (undo/redo changeset)
  256. @param changeset changeset id
  257. @param undo True for undo otherwise redo
  258. @return 1 changeset applied
  259. @return 0 changeset not applied
  260. @return -1 on error
  261. """
  262. if changeset < 0 or changeset > len(self.changesets.keys()):
  263. return -1
  264. if self.changesetEnd < 0:
  265. self.changesetEnd = changeset
  266. ret = 0
  267. actions = self.changesets[changeset]
  268. for action in actions:
  269. add = action['add']
  270. line = action['line']
  271. if (undo and add) or \
  272. (not undo and not add):
  273. if Vect_line_alive(self.poMapInfo, line):
  274. Debug.msg(3, "IVDigit._applyChangeset(): changeset=%d, action=add, line=%d -> deleted",
  275. changeset, line)
  276. Vect_delete_line(self.poMapInfo, line)
  277. ret = 1
  278. else:
  279. Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=add, line=%d dead",
  280. changeset, line)
  281. else: # delete
  282. offset = action['offset']
  283. if not Vect_line_alive(self.poMapInfo, line):
  284. Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d -> added",
  285. changeset, line)
  286. if Vect_restore_line(self.poMapInfo, line, offset) < 0:
  287. return -1
  288. ret = 1
  289. else:
  290. Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d alive",
  291. changeset, line)
  292. return ret
  293. def _addActionsAfter(self, changeset, nlines):
  294. """!Register action after operation
  295. @param changeset changeset id
  296. @param nline number of lines
  297. """
  298. for line in self._display.selected['ids']:
  299. if Vect_line_alive(self.poMapInfo, line):
  300. self._removeActionFromChangeset(changeset, line, add = False)
  301. for line in range(nlines + 1, Vect_get_num_lines(self.poMapInfo)):
  302. if Vect_line_alive(self.poMapInfo, line):
  303. self._addActionToChangeset(changeset, line, add = True)
  304. def _addActionToChangeset(self, changeset, line, add):
  305. """!Add action to changeset
  306. @param changeset id of changeset
  307. @param line feature id
  308. @param add True to add, otherwise delete
  309. """
  310. if not self._checkMap():
  311. return
  312. if not Vect_line_alive(self.poMapInfo, line):
  313. return
  314. offset = Vect_get_line_offset(self.poMapInfo, line)
  315. if not self.changesets.has_key(changeset):
  316. self.changesets[changeset] = list()
  317. self.changesetCurrent = changeset
  318. self.changesets[changeset].append({ 'add' : add,
  319. 'line' : line,
  320. 'offset' : offset })
  321. Debug.msg(3, "IVDigit._addActionToChangeset(): changeset=%d, type=%d, line=%d, offset=%d",
  322. changeset, type, line, offset)
  323. def _removeActionFromChangeset(self, changeset, line, add):
  324. """!Remove action from changeset
  325. @param changeset changeset id
  326. @param line line id
  327. @param add True for add, False for delete
  328. @return number of actions in changeset
  329. @return -1 on error
  330. """
  331. if changeset not in self.changesets.keys():
  332. return -1
  333. alist = self.changesets[changeset]
  334. for action in alist:
  335. if action['add'] == add and action['line'] == line:
  336. alist.remove(action)
  337. return len(alist)
  338. def AddFeature(self, ftype, points):
  339. """!Add new feature
  340. @param ftype feature type (point, line, centroid, boundary)
  341. @param points tuple of points ((x, y), (x, y), ...)
  342. @return new feature id
  343. """
  344. if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
  345. layer = -1 # -> no category
  346. cat = -1
  347. else:
  348. layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
  349. cat = self.SetCategory()
  350. if ftype == 'point':
  351. vtype = GV_POINT
  352. elif ftype == 'line':
  353. vtype = GV_LINE
  354. elif ftype == 'centroid':
  355. vtype = GV_CENTROID
  356. elif ftype == 'boundary':
  357. vtype = GV_BOUNDARY
  358. elif ftype == 'area':
  359. vtype = GV_AREA
  360. else:
  361. GError(parent = self.mapWindow,
  362. message = _("Unknown feature type '%s'") % ftype)
  363. return
  364. if vtype & GV_LINES and len(points) < 2:
  365. GError(parent = self.mapWindow,
  366. message = _("Not enough points for line"))
  367. return
  368. self.toolbar.EnableUndo()
  369. return self._addFeature(vtype, points, layer, cat,
  370. self._getSnapMode(), self._display.GetThreshold())
  371. def DeleteSelectedLines(self):
  372. """!Delete selected features
  373. @return number of deleted features
  374. """
  375. deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
  376. if not self._checkMap():
  377. return -1
  378. n_dblinks = Vect_get_num_dblinks(self.poMapInfo)
  379. Cats_del = None
  380. # collect categories for delete if requested
  381. if deleteRec:
  382. poCats = Vect_new_cats_struct()
  383. poCatsDel = Vect_new_cats_struct()
  384. for i in self._display.selected['ids']:
  385. if Vect_read_line(self.poMapInfo, None, poCats, i) < 0:
  386. Vect_destroy_cats_struct(poCatsDel)
  387. self._error.ReadLine(i)
  388. return -1
  389. cats = poCats.contents
  390. for j in range(cats.n_cats):
  391. Vect_cat_set(poCatsDel, cats.field[j], cats.cat[j])
  392. Vect_destroy_cats_struct(poCats)
  393. # register changeset
  394. changeset = self._addActionsBefore()
  395. poList = self._display.GetSelectedIList()
  396. nlines = Vedit_delete_lines(self.poMapInfo, poList)
  397. Vect_destroy_list(poList)
  398. self._display.selected['ids'] = list()
  399. if nlines > 0 and deleteRec:
  400. handle = dbHandle()
  401. poHandle = pointer(handle)
  402. stmt = dbString()
  403. poStmt = pointer(stmt)
  404. for dblink in range(n_dblinks):
  405. poFi = Vect_get_dblink(self.poMapInfo, dblink)
  406. if poFi is None:
  407. self._error.DbLink(dblink)
  408. return -1
  409. Fi = poFi.contents
  410. poDriver = db_start_driver(Fi.driver)
  411. if poDriver is None:
  412. self._error.Driver(Fi.driver)
  413. return -1
  414. db_init_handle(poHandle)
  415. db_set_handle(poHandle, Fi.database, None)
  416. if db_open_database(poDriver, poHandle) != DB_OK:
  417. self._error.Database(Fi.driver, Fi.database)
  418. return -1
  419. db_init_string(poStmt)
  420. db_set_string(poStmt, "DELETE FROM %s WHERE" % Fi.table)
  421. n_cats = 0;
  422. catsDel = poCatsDel.contents
  423. for c in range(catsDel.n_cats):
  424. if catsDel.field[c] == Fi.number:
  425. if n_cats > 0:
  426. db_append_string(poStmt, " or")
  427. db_append_string(poStmt, " %s = %d" % (Fi.key, catsDel.cat[c]))
  428. n_cats += 1
  429. Vect_cat_del(poCatsDel, Fi.number)
  430. if n_cats and \
  431. db_execute_immediate(poDriver, poStmt) != DB_OK:
  432. self._error.DbExecute(db_get_string(poStmt))
  433. return -1
  434. db_close_database(poDriver)
  435. db_shutdown_driver(poDriver)
  436. if poCatsDel:
  437. Vect_destroy_cats_struct(poCatsDel)
  438. if nlines > 0:
  439. self.toolbar.EnableUndo()
  440. return nlines
  441. def MoveSelectedLines(self, move):
  442. """!Move selected features
  443. @param move direction (x, y)
  444. """
  445. if not self._checkMap():
  446. return -1
  447. thresh = self._display.GetThreshold()
  448. snap = self._getSnapMode()
  449. nlines = Vect_get_num_lines(self.poMapInfo)
  450. # register changeset
  451. changeset = self._addActionsBefore()
  452. poList = self._display.GetSelectedIList()
  453. nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
  454. poList,
  455. move[0], move[1], 0,
  456. snap, thresh)
  457. Vect_destroy_list(poList)
  458. if nlines > 0:
  459. self._addActionsAfter(changeset, nlines)
  460. else:
  461. del self.changesets[changeset]
  462. if nlines > 0 and self._settings['breakLines']:
  463. for i in range(1, nlines):
  464. self._breakLineAtIntersection(nlines + i, None, changeset)
  465. if nlines > 0:
  466. self.toolbar.EnableUndo()
  467. return nlines
  468. def MoveSelectedVertex(self, point, move):
  469. """!Move selected vertex of the line
  470. @param point location point
  471. @param move x,y direction
  472. @return id of new feature
  473. @return 0 vertex not moved (not found, line is not selected)
  474. @return -1 on error
  475. """
  476. if not self._checkMap():
  477. return -1
  478. if len(self._display.selected['ids']) != 1:
  479. return -1
  480. Vect_reset_line(self.poPoints)
  481. Vect_append_point(self.poPoints, point[0], point[1], 0.0)
  482. nlines = Vect_get_num_lines(self.poMapInfo)
  483. changeset = self._addActionsBefore()
  484. # move only first found vertex in bbox
  485. poList = self._display.GetSelectedIList()
  486. moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
  487. poList, self.poPoints,
  488. self._display.GetThreshold(type = 'selectThresh'),
  489. self._display.GetThreshold(),
  490. move[0], move[1], 0.0,
  491. 1, self._getSnapMode())
  492. Vect_destroy_list(poList)
  493. if moved > 0:
  494. self._addActionsAfter(changeset, nlines)
  495. else:
  496. del self.changesets[changeset]
  497. if moved > 0 and self._settings['breakLines']:
  498. self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
  499. None, changeset)
  500. if moved > 0:
  501. self.toolbar.EnableUndo()
  502. return moved
  503. def AddVertex(self, coords):
  504. """!Add new vertex to the selected line/boundary on position 'coords'
  505. @param coords coordinates to add vertex
  506. @return id of new feature
  507. @return 0 nothing changed
  508. @return -1 on failure
  509. """
  510. added = self._ModifyLineVertex(coords, add = True)
  511. if added > 0:
  512. self.toolbar.EnableUndo()
  513. return added
  514. def RemoveVertex(self, coords):
  515. """!Remove vertex from the selected line/boundary on position 'coords'
  516. @param coords coordinates to remove vertex
  517. @return id of new feature
  518. @return 0 nothing changed
  519. @return -1 on failure
  520. """
  521. deleted = self._ModifyLineVertex(coords, add = False)
  522. if deleted > 0:
  523. self.toolbar.EnableUndo()
  524. return deleted
  525. def SplitLine(self, point):
  526. """!Split/break selected line/boundary on given position
  527. @param point point where to split line
  528. @return 1 line modified
  529. @return 0 nothing changed
  530. @return -1 error
  531. """
  532. thresh = self._display.GetThreshold('selectThresh')
  533. if not self._checkMap():
  534. return -1
  535. poList = self._display.GetSelectedIList()
  536. Vect_reset_line(self.poPoints)
  537. Vect_append_point(self.poPoints, point[0], point[1], 0.0)
  538. nlines = Vect_get_num_lines(self.poMapInfo)
  539. changeset = self._addActionsBefore()
  540. ret = Vedit_split_lines(self.poMapInfo, poList,
  541. self.poPoints, thresh, poList)
  542. Vect_destroy_list(poList)
  543. if ret > 0:
  544. self._addActionsAfter(changeset, nlines)
  545. self.toolbar.EnableUndo()
  546. else:
  547. del self.changesets[changeset]
  548. return ret
  549. def EditLine(self, line, coords):
  550. """!Edit existing line/boundary
  551. @param line id of line to be modified
  552. @param coords list of coordinates of modified line
  553. @return feature id of new line
  554. @return -1 on error
  555. """
  556. if self._checkMap():
  557. return -1
  558. try:
  559. lineid = line[0]
  560. except:
  561. lineid = -1
  562. if len(coords) < 2:
  563. self.DeleteSelectedLines()
  564. return 0
  565. ret = self.digit.RewriteLine(lineid, listCoords,
  566. bgmap, self._getSnapMode(),
  567. self._display.GetThreshold())
  568. if ret > 0:
  569. self.toolbar.EnableUndo()
  570. return ret
  571. def FlipLine(self):
  572. """!Flip selected lines/boundaries
  573. @return number of modified lines
  574. @return -1 on error
  575. """
  576. if not self._checkMap():
  577. return -1
  578. nlines = Vect_get_num_lines(self.poMapInfo)
  579. # register changeset
  580. changeset = self._addActionsBefore()
  581. poList = self._display.GetSelectedIList()
  582. ret = Vedit_flip_lines(self.poMapInfo, poList)
  583. Vect_destroy_list(poList)
  584. if ret > 0:
  585. self._addActionsAfter(changeset, nlines)
  586. self.toolbar.EnableUndo()
  587. else:
  588. del self.changesets[changeset]
  589. return ret
  590. def MergeLine(self):
  591. """!Merge selected lines/boundaries
  592. @return number of modified lines
  593. @return -1 on error
  594. """
  595. if not self._checkMap():
  596. return -1
  597. nlines = Vect_get_num_lines(self.poMapInfo)
  598. changeset = self._addActionsBefore()
  599. poList = self._display.GetSelectedIList()
  600. ret = Vedit_merge_lines(self.poMapInfo, poList)
  601. Vect_destroy_list(poList)
  602. if ret > 0:
  603. self._addActionsAfter(changeset, nlines)
  604. self.toolbar.EnableUndo()
  605. else:
  606. del self.changesets[changeset]
  607. return ret
  608. def BreakLine(self):
  609. """!Break selected lines/boundaries
  610. @return number of modified lines
  611. @return -1 on error
  612. """
  613. if not self._checkMap():
  614. return -1
  615. nlines = Vect_get_num_lines(self.poMapInfo)
  616. changeset = self._addActionsBefore()
  617. poList = self._display.GetSelectedIList()
  618. ret = Vect_break_lines_list(self.poMapInfo, poList, None,
  619. GV_LINES, None)
  620. Vect_destroy_list(poList)
  621. if ret > 0:
  622. self._addActionsAfter(changeset, nlines)
  623. self.toolbar.EnableUndo()
  624. else:
  625. del self.changesets[changeset]
  626. return ret
  627. def SnapLine(self):
  628. """!Snap selected lines/boundaries
  629. @return on success
  630. @return -1 on error
  631. """
  632. if not self._checkMap():
  633. return -1
  634. nlines = Vect_get_num_lines(self.poMapInfo)
  635. changeset = self._addActionsBefore()
  636. poList = self._display.GetSelectedIList()
  637. Vect_snap_lines_list(self.poMapInfo, poList,
  638. self._display.GetThreshold(), None)
  639. Vect_destroy_list(poList)
  640. if nlines < Vect_get_num_lines(self.poMapInfo):
  641. self._addActionsAfter(changeset, nlines)
  642. self.toolbar.EnableUndo()
  643. else:
  644. del self.changesets[changeset]
  645. def ConnectLine(self):
  646. """!Connect selected lines/boundaries
  647. @return 1 lines connected
  648. @return 0 lines not connected
  649. @return -1 on error
  650. """
  651. if not self._checkMap():
  652. return -1
  653. nlines = Vect_get_num_lines(self.poMapInfo)
  654. # register changeset
  655. changeset = self._addActionsBefore()
  656. poList = self._display.GetSelectedIList()
  657. ret = Vedit_connect_lines(self.poMapInfo, poList,
  658. self._display.GetThreshold())
  659. Vect_destroy_list(poList)
  660. if ret > 0:
  661. self._addActionsAfter(changeset, nlines)
  662. self.toolbar.EnableUndo()
  663. else:
  664. del self.changesets[changeset]
  665. return ret
  666. def CopyLine(self, ids = []):
  667. """!Copy features from (background) vector map
  668. @param ids list of line ids to be copied
  669. @return number of copied features
  670. @return -1 on error
  671. """
  672. if not self._checkMap():
  673. return -1
  674. nlines = Vect_get_num_lines(self.poMapInfo)
  675. poList = self._display.GetSelectedIList(ids)
  676. ret = Vedit_copy_lines(self.poMapInfo, self.poBgMapInfo,
  677. poList)
  678. Vect_destroy_list(poList)
  679. if ret > 0:
  680. changeset = len(self.changesets)
  681. for line in (range(nlines + 1, Vect_get_num_lines(self.poMapInfo))):
  682. self._addActionToChangeset(changeset, line, add = True)
  683. self.toolbar.EnableUndo()
  684. else:
  685. del self.changesets[changeset]
  686. if ret > 0 and self.poBgMapInfo and self._settings['breakLines']:
  687. for i in range(1, ret):
  688. self._breakLineAtIntersection(nlines + i, None, changeset)
  689. return ret
  690. def CopyCats(self, fromId, toId, copyAttrb=False):
  691. """!Copy given categories to objects with id listed in ids
  692. @param cats ids of 'from' feature
  693. @param ids ids of 'to' feature(s)
  694. @return number of modified features
  695. @return -1 on error
  696. """
  697. if len(fromId) == 0 or len(toId) == 0:
  698. return 0
  699. ret = self.digit.CopyCats(fromId, toId, copyAttrb)
  700. if ret > 0:
  701. self.toolbar.EnableUndo()
  702. return ret
  703. def _selectLinesByQueryThresh(self):
  704. """!Generic method used for SelectLinesByQuery() -- to get
  705. threshold value"""
  706. thresh = 0.0
  707. if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
  708. thresh = UserSettings.Get(group = 'vdigit', key = 'queryLength', subkey = 'thresh')
  709. if UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'than-selection') == 0:
  710. thresh = -1 * thresh
  711. else:
  712. thresh = UserSettings.Get(group = 'vdigit', key = 'queryDangle', subkey = 'thresh')
  713. if UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'than-selection') == 0:
  714. thresh = -1 * thresh
  715. return thresh
  716. def SelectLinesByQuery(self, bbox):
  717. """!Select features by query
  718. @todo layer / 3D
  719. @param bbox bounding box definition
  720. """
  721. if not self._checkMap():
  722. return -1
  723. thresh = self._selectLinesByQueryThresh()
  724. query = QUERY_UNKNOWN
  725. if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
  726. query = QUERY_LENGTH
  727. else:
  728. query = QUERY_DANGLE
  729. ftype = GV_POINTS | GV_LINES # TODO: 3D
  730. layer = 1 # TODO
  731. ids = list()
  732. poList = Vect_new_list()
  733. coList = poList.contents
  734. if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'box'):
  735. Vect_reset_line(self.poPoints)
  736. x1, y1 = bbox[0]
  737. x2, y2 = bbox[1]
  738. z1 = z2 = 0.0
  739. Vect_append_point(self.poPoints, x1, y1, z1)
  740. Vect_append_point(self.poPoints, x2, y1, z2)
  741. Vect_append_point(self.poPoints, x2, y2, z1)
  742. Vect_append_point(self.poPoints, x1, y2, z2)
  743. Vect_append_point(self.poPoints, x1, y1, z1)
  744. Vect_select_lines_by_polygon(self.poMapInfo, self.poPoints, 0, None,
  745. ftype, poList)
  746. if coList.n_values == 0:
  747. return ids
  748. Vedit_select_by_query(self.poMapInfo,
  749. ftype, layer, thresh, query,
  750. poList)
  751. for i in range(coList.n_values):
  752. ids.append(int(coList.value[i]))
  753. Debug.msg(3, "IVDigit.SelectLinesByQuery(): lines=%d", coList.n_values)
  754. Vect_destroy_list(poList)
  755. return ids
  756. def IsVector3D(self):
  757. """!Check if open vector map is 3D
  758. """
  759. if not self._checkMap():
  760. return False
  761. return Vect_is_3d(self.poMapInfo)
  762. def GetLineCats(self, line=-1):
  763. """!Get layer/category pairs from given (selected) line
  764. @param line feature id (-1 for first selected line)
  765. """
  766. return dict(self.digit.GetLineCats(line))
  767. def GetLineLength(self, line):
  768. """!Get line length
  769. @param line feature id
  770. @return line length
  771. @return -1 on error
  772. """
  773. return self.digit.GetLineLength(line)
  774. def GetAreaSize(self, centroid):
  775. """!Get area size
  776. @param centroid centroid id
  777. @return area size
  778. @return -1 on error
  779. """
  780. return self.digit.GetAreaSize(centroid)
  781. def GetAreaPerimeter(self, centroid):
  782. """!Get area perimeter
  783. @param centroid centroid id
  784. @return area size
  785. @return -1 on error
  786. """
  787. return self.digit.GetAreaPerimeter(centroid)
  788. def SetLineCats(self, line, layer, cats, add=True):
  789. """!Set categories for given line and layer
  790. @param line feature id
  791. @param layer layer number (-1 for first selected line)
  792. @param cats list of categories
  793. @param add if True to add, otherwise do delete categories
  794. @return new feature id (feature need to be rewritten)
  795. @return -1 on error
  796. """
  797. ret = self.digit.SetLineCats(line, layer, cats, add)
  798. if ret > 0:
  799. self.toolbar.EnableUndo()
  800. return ret
  801. def GetLayers(self):
  802. """!Get list of layers"""
  803. return self.digit.GetLayers()
  804. def TypeConvForSelectedLines(self):
  805. """!Feature type conversion for selected objects.
  806. Supported conversions:
  807. - point <-> centroid
  808. - line <-> boundary
  809. @return number of modified features
  810. @return -1 on error
  811. """
  812. if not self._checkMap():
  813. return -1
  814. nlines = Vect_get_num_lines(self.poMapInfo)
  815. # register changeset
  816. changeset = self._addActionsBefore()
  817. poList = self._display.GetSelectedIList()
  818. ret = Vedit_chtype_lines(self.poMapInfo, poList)
  819. Vect_destroy_list(poList)
  820. if ret > 0:
  821. self._addActionsAfter(changeset, nlines)
  822. self.toolbar.EnableUndo()
  823. else:
  824. del self.changesets[changeset]
  825. return ret
  826. def Undo(self, level = -1):
  827. """!Undo action
  828. @param level levels to undo (0 to revert all)
  829. @return id of current changeset
  830. """
  831. changesetLast = len(self.changesets.keys()) - 1
  832. if changesetLast < 0:
  833. return changesetLast
  834. if self.changesetCurrent == -2: # value uninitialized
  835. self.changesetCurrent = changesetLast
  836. if level > 0 and self.changesetCurrent < 0:
  837. self.changesetCurrent = 0
  838. if level == 0:
  839. # 0 -> undo all
  840. level = -1 * changesetLast + 1
  841. Debug.msg(2, "Digit.Undo(): changeset_last=%d, changeset_current=%d, level=%d",
  842. changesetLast, self.changesetCurrent, level)
  843. if level < 0: # undo
  844. if self.changesetCurrent + level < -1:
  845. return changesetCurrent;
  846. for changeset in range(self.changesetCurrent, self.changesetCurrent + level, -1):
  847. self._applyChangeset(changeset, undo = True)
  848. elif level > 0: # redo
  849. if self.changesetCurrent + level > len(self.changesets.keys()):
  850. return self.changesetCurrent
  851. for changeset in range(self.changesetCurrent, self.changesetCurrent + level):
  852. self._applyChangeset(changeset, undo = False)
  853. self.changesetCurrent += level
  854. Debug.msg(2, "Digit.Undo(): changeset_current=%d, changeset_last=%d, changeset_end=%d",
  855. self.changesetCurrent, changesetLast, self.changesetEnd)
  856. if self.changesetCurrent == self.changesetEnd:
  857. self.changesetEnd = changesetLast
  858. return -1
  859. self.mapWindow.UpdateMap(render = False)
  860. if self.changesetCurrent < 0: # disable undo tool
  861. self.toolbar.EnableUndo(False)
  862. def ZBulkLines(self, pos1, pos2, start, step):
  863. """!Z-bulk labeling
  864. @param pos1 reference line (start point)
  865. @param pos1 reference line (end point)
  866. @param start starting value
  867. @param step step value
  868. @return number of modified lines
  869. @return -1 on error
  870. """
  871. if not self._checkMap():
  872. return -1
  873. nlines = Vect_get_num_lines(self.poMapInfo)
  874. # register changeset
  875. changeset = self._addActionsBefore()
  876. poList = self._display.GetSelectedIList()
  877. ret = Vedit_bulk_labeling(self.poMapInfo, poList,
  878. pos1[0], pos1[1], pos2[0], pos2[1],
  879. start, step)
  880. Vect_destroy_list(poList)
  881. if ret > 0:
  882. self._addActionsAfter(changeset, nlines)
  883. self.toolbar.EnableUndo()
  884. else:
  885. del self.changesets[changeset]
  886. return ret
  887. def GetDisplay(self):
  888. """!Get display driver instance"""
  889. return self._display
  890. def OpenMap(self, name):
  891. """!Open vector map for editing
  892. @param map name of vector map to be set up
  893. """
  894. Debug.msg (3, "AbstractDigit.SetMapName map=%s" % name)
  895. name, mapset = name.split('@')
  896. self.poMapInfo = self._display.OpenMap(str(name), str(mapset), True)
  897. if self.poMapInfo:
  898. self.InitCats()
  899. return self.poMapInfo
  900. def CloseMap(self):
  901. """!Close currently open vector map
  902. """
  903. if not self._checkMap():
  904. return
  905. self._display.CloseMap()
  906. def InitCats(self):
  907. """!Initialize categories information
  908. @return 0 on success
  909. @return -1 on error
  910. """
  911. self.cats.clear()
  912. if not self._checkMap():
  913. return -1
  914. ndblinks = Vect_get_num_dblinks(self.poMapInfo)
  915. for i in range(ndblinks):
  916. fi = Vect_get_dblink(self.poMapInfo, i).contents
  917. if fi:
  918. self.cats[fi.number] = None
  919. # find max category
  920. nfields = Vect_cidx_get_num_fields(self.poMapInfo)
  921. Debug.msg(2, "wxDigit.InitCats(): nfields=%d", nfields)
  922. for i in range(nfields):
  923. field = Vect_cidx_get_field_number(self.poMapInfo, i)
  924. ncats = Vect_cidx_get_num_cats_by_index(self.poMapInfo, i)
  925. if field <= 0:
  926. continue
  927. for j in range(ncats):
  928. cat = c_int()
  929. type = c_int()
  930. id = c_int()
  931. Vect_cidx_get_cat_by_index(self.poMapInfo, i, j,
  932. byref(cat), byref(type), byref(id))
  933. if self.cats.has_key(field):
  934. if cat > self.cats[field]:
  935. self.cats[field] = cat.value
  936. else:
  937. self.cats[field] = cat.value
  938. Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
  939. # set default values
  940. for field, cat in self.cats.iteritems():
  941. if cat == None:
  942. self.cats[field] = 0 # first category 1
  943. Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
  944. def _checkMap(self):
  945. """!Check if map is open
  946. """
  947. if not self.poMapInfo:
  948. self._error.NoMap()
  949. return False
  950. return True
  951. def _addFeature(self, ftype, coords, layer, cat, snap, threshold):
  952. """!Add new feature to the vector map
  953. @param ftype feature type (GV_POINT, GV_LINE, GV_BOUNDARY, ...)
  954. @coords tuple of coordinates ((x, y), (x, y), ...)
  955. @param layer layer number (-1 for no cat)
  956. @param cat category number
  957. @param snap snap to node/vertex
  958. @param threshold threshold for snapping
  959. @return -1 on error
  960. @return feature id of new feature
  961. """
  962. if not self._checkMap():
  963. return -1
  964. is3D = bool(Vect_is_3d(self.poMapInfo))
  965. Debug.msg(2, "IVDigit._addFeature(): npoints=%d, layer=%d, cat=%d, snap=%d",
  966. len(coords), layer, cat, snap)
  967. if not (ftype & (GV_POINTS | GV_LINES | GV_AREA)): # TODO: 3D
  968. self._error.FeatureType(ftype)
  969. return -1
  970. # set category
  971. Vect_reset_cats(self.poCats)
  972. if layer > 0 and ftype != GV_AREA:
  973. Vect_cat_set(self.poCats, layer, cat)
  974. self.cats[layer] = max(cat, self.cats.get(layer, 1))
  975. # append points
  976. Vect_reset_line(self.poPoints)
  977. for c in coords:
  978. Vect_append_point(self.poPoints, c[0], c[1], 0.0)
  979. if ftype & (GV_BOUNDARY | GV_AREA):
  980. # close boundary
  981. points = self.poPoints.contents
  982. last = points.n_points - 1
  983. if Vect_points_distance(points.x[0], points.x[0], points.z[0],
  984. points.x[last], points.x[last], points.z[last],
  985. is3D) <= threshold:
  986. points.x[last] = points.x[0]
  987. points.y[last] = points.y[0]
  988. points.z[last] = points.z[0]
  989. if snap != NO_SNAP:
  990. # apply snapping (node or vertex)
  991. modeSnap = not (snap == SNAP)
  992. Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
  993. -1, self.poPoints, threshold, modeSnap)
  994. if ftype == GV_AREA:
  995. ltype = GV_BOUNDARY
  996. else:
  997. ltype = ftype
  998. newline = Vect_write_line(self.poMapInfo, ltype, self.poPoints, self.poCats)
  999. if newline < 0:
  1000. self._error.WriteLine()
  1001. return -1
  1002. left = right = -1
  1003. if ftype & GV_AREA:
  1004. # add centroids for left/right area
  1005. bpoints = Vect_new_line_struct()
  1006. cleft = c_int()
  1007. cright = c_int()
  1008. Vect_get_line_areas(self.poMapInfo, newline,
  1009. byref(cleft), byref(cright))
  1010. left = cleft.value
  1011. right = cright.value
  1012. # check if area exists and has no centroid inside
  1013. if layer > 0 and (left > 0 or right > 0):
  1014. Vect_cat_set(self.poCats, layer, cat)
  1015. self.cats[layer] = max(cat, self.cats.get(layer, 0))
  1016. x = c_double()
  1017. y = c_double()
  1018. if left > 0 and \
  1019. Vect_get_area_centroid(self.poMapInfo, left) == 0:
  1020. if Vect_get_area_points(self.poMapInfo, left, bpoints) > 0 and \
  1021. Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
  1022. Vect_reset_line(bpoints)
  1023. Vect_append_point(bpoints, x.value, y.value, 0.0)
  1024. if Vect_write_line(self.poMapInfo, GV_CENTROID,
  1025. bpoints, self.poCats) < 0:
  1026. self._error.WriteLine()
  1027. return -1
  1028. if right > 0 and \
  1029. Vect_get_area_centroid(self.poMapInfo, right) == 0:
  1030. if Vect_get_area_points(byref(self.poMapInfo), right, bpoints) > 0 and \
  1031. Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
  1032. Vect_reset_line(bpoints)
  1033. Vect_append_point(bpoints, x.value, y.value, 0.0)
  1034. if Vect_write_line(byref(self.poMapInfo), GV_CENTROID,
  1035. bpoints, self.poCats) < 0:
  1036. self._error.WriteLine()
  1037. return -1
  1038. Vect_destroy_line_struct(bpoints)
  1039. # register changeset
  1040. changeset = len(self.changesets)
  1041. self._addActionToChangeset(changeset, newline, add = True)
  1042. # break at intersection
  1043. if self._settings['breakLines']:
  1044. self._breakLineAtIntersection(newline, self.poPoints, changeset)
  1045. return newline
  1046. def _ModifyLineVertex(self, coords, add = True):
  1047. """!Add or remove vertex
  1048. Shape of line/boundary is not changed when adding new vertex.
  1049. @param coords coordinates of point
  1050. @param add True to add, False to remove
  1051. @return id id of the new feature
  1052. @return 0 nothing changed
  1053. @return -1 error
  1054. """
  1055. if not self._checkMap():
  1056. return -1
  1057. selected = self._display.selected
  1058. if len(selected['ids']) != 1:
  1059. return 0
  1060. poList = self._display.GetSelectedIList()
  1061. Vect_reset_line(self.poPoints)
  1062. Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
  1063. nlines = Vect_get_num_lines(self.poMapInfo)
  1064. thresh = self._display.GetThreshold(type = 'selectThresh')
  1065. changeset = self._addActionsBefore()
  1066. if add:
  1067. ret = Vedit_add_vertex(self.poMapInfo, poList,
  1068. self.poPoints, thresh)
  1069. else:
  1070. ret = Vedit_remove_vertex(self.poMapInfo, poList,
  1071. self.poPoints, thresh)
  1072. Vect_destroy_list(poList)
  1073. if ret > 0:
  1074. self._addActionsAfter(changeset, nlines)
  1075. else:
  1076. del self.changesets[changeset]
  1077. if not add and ret > 0 and self._settings['breakLines']:
  1078. self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
  1079. None, changeset)
  1080. return nlines + 1 # feature is write at the end of the file
  1081. def GetLineCats(self, line):
  1082. """!Get list of layer/category(ies) for selected feature.
  1083. @param line feature id (-1 for first selected feature)
  1084. @return list of layer/cats
  1085. """
  1086. ret = dict()
  1087. if not self._checkMap():
  1088. return ret
  1089. if line == -1 and len(self._display.selected['ids']) < 1:
  1090. return ret
  1091. if line == -1:
  1092. line = self._display.selected['ids'][0]
  1093. if not Vect_line_alive(self.poMapInfo, line):
  1094. self._error.DeadLine(line)
  1095. return ret
  1096. if Vect_read_line(self.poMapInfo, None, self.poCats, line) < 0:
  1097. self._error.ReadLine(line)
  1098. return ret
  1099. cats = self.poCats.contents
  1100. for i in range(cats.n_cats):
  1101. field = cats.field[i]
  1102. if field not in ret:
  1103. ret[field] = list()
  1104. ret[field].append(cats.cat[i])
  1105. return ret
  1106. def GetLayers(self):
  1107. """!Get list of layers
  1108. Requires self.InitCats() to be called.
  1109. @return list of layers
  1110. """
  1111. return self.cats.keys()
  1112. def UpdateSettings(self):
  1113. """!Update digit (and display) settings
  1114. """
  1115. self._display.UpdateSettings()
  1116. self._settings['breakLines'] = bool(UserSettings.Get(group = 'vdigit', key = "breakLines",
  1117. subkey = 'enabled'))
  1118. def SetCategory(self):
  1119. """!Update self.cats based on settings"""
  1120. sel = UserSettings.Get(group = 'vdigit', key = 'categoryMode', subkey = 'selection')
  1121. cat = None
  1122. if sel == 0: # next to usep
  1123. cat = self._setCategoryNextToUse()
  1124. elif sel == 1:
  1125. cat = UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value')
  1126. if cat:
  1127. layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
  1128. self.cats[layer] = cat
  1129. return cat
  1130. def _setCategoryNextToUse(self):
  1131. """!Find maximum category number for the given layer and
  1132. update the settings
  1133. @return category to be used
  1134. """
  1135. # get max category number for given layer and update the settings
  1136. layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
  1137. cat = self.cats.get(layer, 0) + 1
  1138. UserSettings.Set(group = 'vdigit', key = 'category', subkey = 'value',
  1139. value = cat)
  1140. Debug.msg(1, "IVDigit._setCategoryNextToUse(): cat=%d", cat)
  1141. return cat
  1142. def SelectLinesFromBackgroundMap(self, bbox):
  1143. """!Select features from background map
  1144. @param bbox bounding box definition
  1145. @return list of selected feature ids
  1146. """
  1147. ret = list()
  1148. # try select features by Box
  1149. ids = self._display.SelectLinesByBox(bbox, poMapInfo = self.poBgMapInfo)
  1150. if not ids:
  1151. ids = [self._display.SelectLineByPoint(bbox[0], poMapInfo = self.poBgMapInfo)['line'], ]
  1152. return ids
  1153. def GetUndoLevel(self):
  1154. """!Get undo level (number of active changesets)
  1155. Note: Changesets starts wiht 0
  1156. """
  1157. return self.changesetCurrent