wxvdigit.py 52 KB

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