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