wxdigit.py 60 KB

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