wxdigit.py 68 KB

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