wxvdigit.py 59 KB

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