wxdigit.py 55 KB

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