wxvdigit.py 44 KB

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