wxvdigit.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577
  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 tuple (number of added features, feature ids)
  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 (-1, None)
  364. if vtype & GV_LINES and len(points) < 2:
  365. GError(parent = self.mapWindow,
  366. message = _("Not enough points for line"))
  367. return (-1, None)
  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. if not self._checkMap():
  774. return -1
  775. if not Vect_line_alive(self.poMapInfo, line):
  776. return -1
  777. ltype = Vect_read_line(self.poMapInfo, self.poPoints, None, line)
  778. if ltype < 0:
  779. self._error.ReadLine(line)
  780. return ret
  781. length = -1
  782. if ltype & GV_LINES: # lines & boundaries
  783. length = Vect_line_length(self.poPoints)
  784. return length
  785. def GetAreaSize(self, centroid):
  786. """!Get area size
  787. @param centroid centroid id
  788. @return area size
  789. @return -1 on error
  790. """
  791. if not self._checkMap():
  792. return -1
  793. ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
  794. if ltype < 0:
  795. self._error.ReadLine(line)
  796. return ret
  797. if ltype != GV_CENTROID:
  798. return -1
  799. area = Vect_get_centroid_area(self.poMapInfo, centroid)
  800. size = -1
  801. if area > 0:
  802. if not Vect_area_alive(self.poMapInfo, area):
  803. return size
  804. size = Vect_get_area_area(self.poMapInfo, area)
  805. return size
  806. def GetAreaPerimeter(self, centroid):
  807. """!Get area perimeter
  808. @param centroid centroid id
  809. @return area size
  810. @return -1 on error
  811. """
  812. if not self._checkMap():
  813. return -1
  814. ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
  815. if ltype < 0:
  816. self._error.ReadLine(line)
  817. return ret
  818. if ltype != GV_CENTROID:
  819. return -1
  820. area = Vect_get_centroid_area(self.poMapInfo, centroid)
  821. perimeter = -1
  822. if area > 0:
  823. if not Vect_area_alive(self,poMapInfo, area):
  824. return -1
  825. Vect_get_area_points(self.poMapInfo, area, self.poPoints)
  826. perimeter = Vect_area_perimeter(self.poPoints)
  827. return perimeter
  828. def SetLineCats(self, line, layer, cats, add=True):
  829. """!Set categories for given line and layer
  830. @param line feature id
  831. @param layer layer number (-1 for first selected line)
  832. @param cats list of categories
  833. @param add if True to add, otherwise do delete categories
  834. @return new feature id (feature need to be rewritten)
  835. @return -1 on error
  836. """
  837. ret = self.digit.SetLineCats(line, layer, cats, add)
  838. if ret > 0:
  839. self.toolbar.EnableUndo()
  840. return ret
  841. def GetLayers(self):
  842. """!Get list of layers"""
  843. return self.digit.GetLayers()
  844. def TypeConvForSelectedLines(self):
  845. """!Feature type conversion for selected objects.
  846. Supported conversions:
  847. - point <-> centroid
  848. - line <-> boundary
  849. @return number of modified features
  850. @return -1 on error
  851. """
  852. if not self._checkMap():
  853. return -1
  854. nlines = Vect_get_num_lines(self.poMapInfo)
  855. # register changeset
  856. changeset = self._addActionsBefore()
  857. poList = self._display.GetSelectedIList()
  858. ret = Vedit_chtype_lines(self.poMapInfo, poList)
  859. Vect_destroy_list(poList)
  860. if ret > 0:
  861. self._addActionsAfter(changeset, nlines)
  862. self.toolbar.EnableUndo()
  863. else:
  864. del self.changesets[changeset]
  865. return ret
  866. def Undo(self, level = -1):
  867. """!Undo action
  868. @param level levels to undo (0 to revert all)
  869. @return id of current changeset
  870. """
  871. changesetLast = len(self.changesets.keys()) - 1
  872. if changesetLast < 0:
  873. return changesetLast
  874. if self.changesetCurrent == -2: # value uninitialized
  875. self.changesetCurrent = changesetLast
  876. if level > 0 and self.changesetCurrent < 0:
  877. self.changesetCurrent = 0
  878. if level == 0:
  879. # 0 -> undo all
  880. level = -1 * changesetLast + 1
  881. Debug.msg(2, "Digit.Undo(): changeset_last=%d, changeset_current=%d, level=%d",
  882. changesetLast, self.changesetCurrent, level)
  883. if level < 0: # undo
  884. if self.changesetCurrent + level < -1:
  885. return changesetCurrent;
  886. for changeset in range(self.changesetCurrent, self.changesetCurrent + level, -1):
  887. self._applyChangeset(changeset, undo = True)
  888. elif level > 0: # redo
  889. if self.changesetCurrent + level > len(self.changesets.keys()):
  890. return self.changesetCurrent
  891. for changeset in range(self.changesetCurrent, self.changesetCurrent + level):
  892. self._applyChangeset(changeset, undo = False)
  893. self.changesetCurrent += level
  894. Debug.msg(2, "Digit.Undo(): changeset_current=%d, changeset_last=%d, changeset_end=%d",
  895. self.changesetCurrent, changesetLast, self.changesetEnd)
  896. if self.changesetCurrent == self.changesetEnd:
  897. self.changesetEnd = changesetLast
  898. return -1
  899. self.mapWindow.UpdateMap(render = False)
  900. if self.changesetCurrent < 0: # disable undo tool
  901. self.toolbar.EnableUndo(False)
  902. def ZBulkLines(self, pos1, pos2, start, step):
  903. """!Z-bulk labeling
  904. @param pos1 reference line (start point)
  905. @param pos1 reference line (end point)
  906. @param start starting value
  907. @param step step value
  908. @return number of modified lines
  909. @return -1 on error
  910. """
  911. if not self._checkMap():
  912. return -1
  913. nlines = Vect_get_num_lines(self.poMapInfo)
  914. # register changeset
  915. changeset = self._addActionsBefore()
  916. poList = self._display.GetSelectedIList()
  917. ret = Vedit_bulk_labeling(self.poMapInfo, poList,
  918. pos1[0], pos1[1], pos2[0], pos2[1],
  919. start, step)
  920. Vect_destroy_list(poList)
  921. if ret > 0:
  922. self._addActionsAfter(changeset, nlines)
  923. self.toolbar.EnableUndo()
  924. else:
  925. del self.changesets[changeset]
  926. return ret
  927. def GetDisplay(self):
  928. """!Get display driver instance"""
  929. return self._display
  930. def OpenMap(self, name):
  931. """!Open vector map for editing
  932. @param map name of vector map to be set up
  933. """
  934. Debug.msg (3, "AbstractDigit.SetMapName map=%s" % name)
  935. name, mapset = name.split('@')
  936. self.poMapInfo = self._display.OpenMap(str(name), str(mapset), True)
  937. if self.poMapInfo:
  938. self.InitCats()
  939. return self.poMapInfo
  940. def CloseMap(self):
  941. """!Close currently open vector map
  942. """
  943. if not self._checkMap():
  944. return
  945. self._display.CloseMap()
  946. def InitCats(self):
  947. """!Initialize categories information
  948. @return 0 on success
  949. @return -1 on error
  950. """
  951. self.cats.clear()
  952. if not self._checkMap():
  953. return -1
  954. ndblinks = Vect_get_num_dblinks(self.poMapInfo)
  955. for i in range(ndblinks):
  956. fi = Vect_get_dblink(self.poMapInfo, i).contents
  957. if fi:
  958. self.cats[fi.number] = None
  959. # find max category
  960. nfields = Vect_cidx_get_num_fields(self.poMapInfo)
  961. Debug.msg(2, "wxDigit.InitCats(): nfields=%d", nfields)
  962. for i in range(nfields):
  963. field = Vect_cidx_get_field_number(self.poMapInfo, i)
  964. ncats = Vect_cidx_get_num_cats_by_index(self.poMapInfo, i)
  965. if field <= 0:
  966. continue
  967. for j in range(ncats):
  968. cat = c_int()
  969. type = c_int()
  970. id = c_int()
  971. Vect_cidx_get_cat_by_index(self.poMapInfo, i, j,
  972. byref(cat), byref(type), byref(id))
  973. if self.cats.has_key(field):
  974. if cat > self.cats[field]:
  975. self.cats[field] = cat.value
  976. else:
  977. self.cats[field] = cat.value
  978. Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
  979. # set default values
  980. for field, cat in self.cats.iteritems():
  981. if cat == None:
  982. self.cats[field] = 0 # first category 1
  983. Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
  984. def _checkMap(self):
  985. """!Check if map is open
  986. """
  987. if not self.poMapInfo:
  988. self._error.NoMap()
  989. return False
  990. return True
  991. def _addFeature(self, ftype, coords, layer, cat, snap, threshold):
  992. """!Add new feature(s) to the vector map
  993. @param ftype feature type (GV_POINT, GV_LINE, GV_BOUNDARY, ...)
  994. @coords tuple of coordinates ((x, y), (x, y), ...)
  995. @param layer layer number (-1 for no cat)
  996. @param cat category number
  997. @param snap snap to node/vertex
  998. @param threshold threshold for snapping
  999. @return tuple (number of added features, list of fids)
  1000. @return number of features -1 on error
  1001. """
  1002. fids = list()
  1003. if not self._checkMap():
  1004. return (-1, None)
  1005. is3D = bool(Vect_is_3d(self.poMapInfo))
  1006. Debug.msg(2, "IVDigit._addFeature(): npoints=%d, layer=%d, cat=%d, snap=%d",
  1007. len(coords), layer, cat, snap)
  1008. if not (ftype & (GV_POINTS | GV_LINES | GV_AREA)): # TODO: 3D
  1009. self._error.FeatureType(ftype)
  1010. return (-1, None)
  1011. # set category
  1012. Vect_reset_cats(self.poCats)
  1013. if layer > 0 and ftype != GV_AREA:
  1014. Vect_cat_set(self.poCats, layer, cat)
  1015. self.cats[layer] = max(cat, self.cats.get(layer, 1))
  1016. # append points
  1017. Vect_reset_line(self.poPoints)
  1018. for c in coords:
  1019. Vect_append_point(self.poPoints, c[0], c[1], 0.0)
  1020. if ftype & (GV_BOUNDARY | GV_AREA):
  1021. # close boundary
  1022. points = self.poPoints.contents
  1023. last = points.n_points - 1
  1024. if Vect_points_distance(points.x[0], points.x[0], points.z[0],
  1025. points.x[last], points.x[last], points.z[last],
  1026. is3D) <= threshold:
  1027. points.x[last] = points.x[0]
  1028. points.y[last] = points.y[0]
  1029. points.z[last] = points.z[0]
  1030. if snap != NO_SNAP:
  1031. # apply snapping (node or vertex)
  1032. modeSnap = not (snap == SNAP)
  1033. Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
  1034. -1, self.poPoints, threshold, modeSnap)
  1035. if ftype == GV_AREA:
  1036. ltype = GV_BOUNDARY
  1037. else:
  1038. ltype = ftype
  1039. newline = Vect_write_line(self.poMapInfo, ltype, self.poPoints, self.poCats)
  1040. if newline < 0:
  1041. self._error.WriteLine()
  1042. return (-1, None)
  1043. else:
  1044. fids.append(newline)
  1045. left = right = -1
  1046. if ftype & GV_AREA:
  1047. # add centroids for left/right area
  1048. bpoints = Vect_new_line_struct()
  1049. cleft = c_int()
  1050. cright = c_int()
  1051. Vect_get_line_areas(self.poMapInfo, newline,
  1052. byref(cleft), byref(cright))
  1053. left = cleft.value
  1054. right = cright.value
  1055. # check if area exists and has no centroid inside
  1056. if layer > 0 and (left > 0 or right > 0):
  1057. Vect_cat_set(self.poCats, layer, cat)
  1058. self.cats[layer] = max(cat, self.cats.get(layer, 0))
  1059. x = c_double()
  1060. y = c_double()
  1061. if left > 0 and \
  1062. Vect_get_area_centroid(self.poMapInfo, left) == 0:
  1063. if Vect_get_area_points(self.poMapInfo, left, bpoints) > 0 and \
  1064. Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
  1065. Vect_reset_line(bpoints)
  1066. Vect_append_point(bpoints, x.value, y.value, 0.0)
  1067. newline = Vect_write_line(self.poMapInfo, GV_CENTROID,
  1068. bpoints, self.poCats)
  1069. if newline < 0:
  1070. self._error.WriteLine()
  1071. return (len(fids), fids)
  1072. else:
  1073. fids.append(newline)
  1074. if right > 0 and \
  1075. Vect_get_area_centroid(self.poMapInfo, right) == 0:
  1076. if Vect_get_area_points(byref(self.poMapInfo), right, bpoints) > 0 and \
  1077. Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
  1078. Vect_reset_line(bpoints)
  1079. Vect_append_point(bpoints, x.value, y.value, 0.0)
  1080. newline = Vect_write_line(byref(self.poMapInfo), GV_CENTROID,
  1081. bpoints, self.poCats)
  1082. if newline < 0:
  1083. self._error.WriteLine()
  1084. return (len(fids, fids))
  1085. else:
  1086. fids.append(newline)
  1087. Vect_destroy_line_struct(bpoints)
  1088. # register changeset
  1089. changeset = len(self.changesets)
  1090. self._addActionToChangeset(changeset, newline, add = True)
  1091. # break at intersection
  1092. if self._settings['breakLines']:
  1093. self._breakLineAtIntersection(newline, self.poPoints, changeset)
  1094. return (len(fids), fids)
  1095. def _ModifyLineVertex(self, coords, add = True):
  1096. """!Add or remove vertex
  1097. Shape of line/boundary is not changed when adding new vertex.
  1098. @param coords coordinates of point
  1099. @param add True to add, False to remove
  1100. @return id id of the new feature
  1101. @return 0 nothing changed
  1102. @return -1 error
  1103. """
  1104. if not self._checkMap():
  1105. return -1
  1106. selected = self._display.selected
  1107. if len(selected['ids']) != 1:
  1108. return 0
  1109. poList = self._display.GetSelectedIList()
  1110. Vect_reset_line(self.poPoints)
  1111. Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
  1112. nlines = Vect_get_num_lines(self.poMapInfo)
  1113. thresh = self._display.GetThreshold(type = 'selectThresh')
  1114. changeset = self._addActionsBefore()
  1115. if add:
  1116. ret = Vedit_add_vertex(self.poMapInfo, poList,
  1117. self.poPoints, thresh)
  1118. else:
  1119. ret = Vedit_remove_vertex(self.poMapInfo, poList,
  1120. self.poPoints, thresh)
  1121. Vect_destroy_list(poList)
  1122. if ret > 0:
  1123. self._addActionsAfter(changeset, nlines)
  1124. else:
  1125. del self.changesets[changeset]
  1126. if not add and ret > 0 and self._settings['breakLines']:
  1127. self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
  1128. None, changeset)
  1129. return nlines + 1 # feature is write at the end of the file
  1130. def GetLineCats(self, line):
  1131. """!Get list of layer/category(ies) for selected feature.
  1132. @param line feature id (-1 for first selected feature)
  1133. @return list of layer/cats
  1134. """
  1135. ret = dict()
  1136. if not self._checkMap():
  1137. return ret
  1138. if line == -1 and len(self._display.selected['ids']) < 1:
  1139. return ret
  1140. if line == -1:
  1141. line = self._display.selected['ids'][0]
  1142. if not Vect_line_alive(self.poMapInfo, line):
  1143. self._error.DeadLine(line)
  1144. return ret
  1145. if Vect_read_line(self.poMapInfo, None, self.poCats, line) < 0:
  1146. self._error.ReadLine(line)
  1147. return ret
  1148. cats = self.poCats.contents
  1149. for i in range(cats.n_cats):
  1150. field = cats.field[i]
  1151. if field not in ret:
  1152. ret[field] = list()
  1153. ret[field].append(cats.cat[i])
  1154. return ret
  1155. def GetLayers(self):
  1156. """!Get list of layers
  1157. Requires self.InitCats() to be called.
  1158. @return list of layers
  1159. """
  1160. return self.cats.keys()
  1161. def UpdateSettings(self):
  1162. """!Update digit (and display) settings
  1163. """
  1164. self._display.UpdateSettings()
  1165. self._settings['breakLines'] = bool(UserSettings.Get(group = 'vdigit', key = "breakLines",
  1166. subkey = 'enabled'))
  1167. def SetCategory(self):
  1168. """!Update self.cats based on settings"""
  1169. sel = UserSettings.Get(group = 'vdigit', key = 'categoryMode', subkey = 'selection')
  1170. cat = None
  1171. if sel == 0: # next to usep
  1172. cat = self._setCategoryNextToUse()
  1173. elif sel == 1:
  1174. cat = UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value')
  1175. if cat:
  1176. layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
  1177. self.cats[layer] = cat
  1178. return cat
  1179. def _setCategoryNextToUse(self):
  1180. """!Find maximum category number for the given layer and
  1181. update the settings
  1182. @return category to be used
  1183. """
  1184. # get max category number for given layer and update the settings
  1185. layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
  1186. cat = self.cats.get(layer, 0) + 1
  1187. UserSettings.Set(group = 'vdigit', key = 'category', subkey = 'value',
  1188. value = cat)
  1189. Debug.msg(1, "IVDigit._setCategoryNextToUse(): cat=%d", cat)
  1190. return cat
  1191. def SelectLinesFromBackgroundMap(self, bbox):
  1192. """!Select features from background map
  1193. @param bbox bounding box definition
  1194. @return list of selected feature ids
  1195. """
  1196. ret = list()
  1197. # try select features by Box
  1198. ids = self._display.SelectLinesByBox(bbox, poMapInfo = self.poBgMapInfo)
  1199. if not ids:
  1200. ids = [self._display.SelectLineByPoint(bbox[0], poMapInfo = self.poBgMapInfo)['line'], ]
  1201. return ids
  1202. def GetUndoLevel(self):
  1203. """!Get undo level (number of active changesets)
  1204. Note: Changesets starts wiht 0
  1205. """
  1206. return self.changesetCurrent