wxdigit.py 55 KB

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