wxvdigit.py 60 KB

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