GRASS Programmer's Manual  6.4.2(2012)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
wxvdriver.py
Go to the documentation of this file.
1 """!
2 @package wxvdriver.py
3 
4 @brief wxGUI vector digitizer (display driver)
5 
6 Code based on wxVdigit C++ component from GRASS 6.4.0
7 (gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
8 
9 List of classes:
10  - DisplayDriver
11 
12 (C) 2007-2011 by the GRASS Development Team
13 
14 This program is free software under the GNU General Public License
15 (>=v2). Read the file COPYING that comes with GRASS for details.
16 
17 @author Martin Landa <landa.martin gmail.com>
18 """
19 
20 import math
21 import locale
22 
23 import wx
24 
25 from debug import Debug
26 from preferences import globalSettings as UserSettings
27 
28 from grass.lib.gis import *
29 from grass.lib.vector import *
30 from grass.lib.vedit import *
31 
32 log = None
33 progress = None
34 
35 def print_error(msg, type):
36  """!Redirect stderr"""
37  global log
38  if log:
39  log.write(msg)
40  else:
41  print msg
42 
43  return 0
44 
45 def print_progress(value):
46  """!Redirect progress info"""
47  global progress
48  if progress:
49  progress.SetValue(value)
50  else:
51  print value
52 
53  return 0
54 
55 errtype = CFUNCTYPE(UNCHECKED(c_int), String, c_int)
56 errfunc = errtype(print_error)
57 pertype = CFUNCTYPE(UNCHECKED(c_int), c_int)
58 perfunc = pertype(print_progress)
59 
61  def __init__(self, device, deviceTmp, mapObj, window, glog, gprogress):
62  """!Display driver used by vector digitizer
63 
64  @param device wx.PseudoDC device where to draw vector objects
65  @param deviceTmp wx.PseudoDC device where to draw temporary vector objects
66  @param mapOng Map Object (render.Map)
67  @param windiow parent window for dialogs
68  @param glog logging device (None to discard messages)
69  @param gprogress progress bar device (None to discard message)
70  """
71  global errfunc, perfunc, log, progress
72  log = glog
73  progress = gprogress
74 
75  G_gisinit('wxvdigit')
76  locale.setlocale(locale.LC_NUMERIC, 'C')
77  G_set_error_routine(errfunc)
78  G_set_percent_routine(perfunc)
79 
80  self.mapInfo = None # open vector map (Map_Info structure)
81  self.poMapInfo = None # pointer to self.mapInfo
82  self.is3D = False # is open vector map 3D
83 
84  self.dc = device # PseudoDC devices
85  self.dcTmp = deviceTmp
86  self.mapObj = mapObj
87  self.region = mapObj.GetCurrentRegion()
88  self.window = window
89  self.log = log # log device
90 
91  self.firstNode = True # track PseudoDC Id of selected features
92  self.lastNodeId = -1
93 
94  # GRASS lib
97 
98  # selected objects
99  self.selected = {
100  'field' : -1, # field number
101  'cats' : list(), # list of cats
102  'ids' : list(), # list of ids
103  'idsDupl' : list(), # list of duplicated features
104  }
105 
106  # digitizer settings
107  self.settings = {
108  'highlight' : None,
109  'highlightDupl' : { 'enabled' : False,
110  'color' : None },
111  'point' : { 'enabled' : False,
112  'color' : None },
113  'line' : { 'enabled' : False,
114  'color' : None },
115  'boundaryNo' : { 'enabled' : False,
116  'color' : None },
117  'boundaryOne' : { 'enabled' : False,
118  'color' : None },
119  'boundaryTwo' : { 'enabled' : False,
120  'color' : None },
121  'centroidIn' : { 'enabled' : False,
122  'color' : None },
123  'centroidOut' : { 'enabled' : False,
124  'color' : None },
125  'centroidDup' : { 'enabled' : False,
126  'color' : None },
127  'nodeOne' : { 'enabled' : False,
128  'color' : None },
129  'nodeTwo' : { 'enabled' : False,
130  'color' : None },
131  'vertex' : { 'enabled' : False,
132  'color' : None },
133  'area' : { 'enabled' : False,
134  'color' : None },
135  'direction' : { 'enabled' : False,
136  'color' : None },
137  'lineWidth' : -1, # screen units
138  }
139 
140  # topology
141  self._resetTopology()
142 
143  self._drawSelected = False
144  self._drawSegments = False
145 
146  self.UpdateSettings()
147 
148  def __del__(self):
149  """!Close currently open vector map"""
152 
153  if self.poMapInfo:
154  self.CloseMap()
155 
158 
159  def _resetTopology(self):
160  """!Reset topology dict
161  """
162  self.topology = {
163  'highlight' : 0,
164  'point' : 0,
165  'line' : 0,
166  'boundaryNo' : 0,
167  'boundaryOne' : 0,
168  'boundaryTwo' : 0,
169  'centroidIn' : 0,
170  'centroidOut' : 0,
171  'centroidDup' : 0,
172  'nodeOne' : 0,
173  'nodeTwo' : 0,
174  'vertex' : 0,
175  }
176 
177  def _cell2Pixel(self, east, north, elev):
178  """!Conversion from geographic coordinates (east, north)
179  to screen (x, y)
180 
181  @todo 3D stuff...
182 
183  @param east, north, elev geographical coordinates
184 
185  @return x, y screen coordinates (integer)
186  """
187  map_res = max(self.region['ewres'], self.region['nsres'])
188  w = self.region['center_easting'] - (self.mapObj.width / 2) * map_res
189  n = self.region['center_northing'] + (self.mapObj.height / 2) * map_res
190 
191  return int((east - w) / map_res), int((n - north) / map_res)
192 
193  def _drawCross(self, pdc, point, size = 5):
194  """!Draw cross symbol of given size to device content
195 
196  Used for points, nodes, vertices
197 
198  @param[in,out] PseudoDC where to draw
199  @param point coordinates of center
200  @param size size of the cross symbol
201 
202  @return 0 on success
203  @return -1 on failure
204  """
205  if not pdc or not point:
206  return -1
207 
208  pdc.DrawLine(point.x - size, point.y, point.x + size, point.y)
209  pdc.DrawLine(point.x, point.y - size, point.x, point.y + size)
210 
211  return 0
212 
213  def _drawObject(self, robj):
214  """!Draw given object to the device
215 
216  The object is defined as robject() from vedit.h.
217 
218  @param robj object to be rendered
219 
220  @return 1 on success
221  @return -1 on failure (vector feature marked as dead, etc.)
222  """
223  if not self.dc or not self.dcTmp:
224  return -1
225 
226  Debug.msg(3, "_drawObject(): type=%d npoints=%d", robj.type, robj.npoints)
227  brush = None
228  if self._isSelected(robj.fid):
229  pdc = self.dcTmp
230  if self.settings['highlightDupl']['enabled'] and self._isDuplicated(robj.fid):
231  pen = wx.Pen(self.settings['highlightDupl']['color'], self.settings['lineWidth'], wx.SOLID)
232  else:
233  pen = wx.Pen(self.settings['highlight'], self.settings['lineWidth'], wx.SOLID)
234 
235  dcId = 1
236  self.topology['highlight'] += 1
237  if not self._drawSelected:
238  return
239  else:
240  pdc = self.dc
241  pen, brush = self._definePen(robj.type)
242  dcId = 0
243 
244  pdc.SetPen(pen)
245  if brush:
246  pdc.SetBrush(brush)
247 
248  if robj.type & (TYPE_POINT | TYPE_CENTROIDIN | TYPE_CENTROIDOUT | TYPE_CENTROIDDUP |
249  TYPE_NODEONE | TYPE_NODETWO | TYPE_VERTEX): # -> point
250  if dcId > 0:
251  if robj.type == TYPE_VERTEX:
252  dcId = 3 # first vertex
253  elif robj.type & (TYPE_NODEONE | TYPE_NODETWO):
254  if self.firstNode:
255  dcId = 1
256  self.firstNode = False
257  else:
258  dcId = self.lastNodeId
259 
260  for i in range(robj.npoints):
261  p = robj.point[i]
262  if dcId > 0:
263  pdc.SetId(dcId)
264  dcId += 2
265  self._drawCross(pdc, p)
266  else:
267  if dcId > 0 and self._drawSegments:
268  self.fisrtNode = True
269  self.lastNodeId = robj.npoints * 2 - 1
270  dcId = 2 # first segment
271  i = 0
272  while i < robj.npoints - 1:
273  point_beg = wx.Point(robj.point[i].x, robj.point[i].y)
274  point_end = wx.Point(robj.point[i+1].x, robj.point[i+1].y)
275  pdc.SetId(dcId) # set unique id & set bbox for each segment
276  pdc.SetPen(pen)
277  pdc.SetIdBounds(dcId - 1, wx.Rect(point_beg.x, point_beg.y, 0, 0))
278  pdc.SetIdBounds(dcId, wx.RectPP(point_beg, point_end))
279  pdc.DrawLine(point_beg.x, point_beg.y,
280  point_end.x, point_end.y)
281  i += 1
282  dcId += 2
283  pdc.SetIdBounds(dcId - 1, wx.Rect(robj.point[robj.npoints - 1].x,
284  robj.point[robj.npoints - 1].y,
285  0, 0))
286  else:
287  points = list()
288  for i in range(robj.npoints):
289  p = robj.point[i]
290  points.append(wx.Point(p.x, p.y))
291 
292  if robj.type == TYPE_AREA:
293  pdc.DrawPolygon(points)
294  else:
295  pdc.DrawLines(points)
296 
297  def _definePen(self, rtype):
298  """!Define pen/brush based on rendered object)
299 
300  Updates also self.topology dict
301 
302  @return pen, brush
303  """
304  if rtype == TYPE_POINT:
305  key = 'point'
306  elif rtype == TYPE_LINE:
307  key = 'line'
308  elif rtype == TYPE_BOUNDARYNO:
309  key = 'boundaryNo'
310  elif rtype == TYPE_BOUNDARYTWO:
311  key = 'boundaryTwo'
312  elif rtype == TYPE_BOUNDARYONE:
313  key = 'boundaryOne'
314  elif rtype == TYPE_CENTROIDIN:
315  key = 'centroidIn'
316  elif rtype == TYPE_CENTROIDOUT:
317  key = 'centroidOut'
318  elif rtype == TYPE_CENTROIDDUP:
319  key = 'centroidDup'
320  elif rtype == TYPE_NODEONE:
321  key = 'nodeOne'
322  elif rtype == TYPE_NODETWO:
323  key = 'nodeTwo'
324  elif rtype == TYPE_VERTEX:
325  key = 'vertex'
326  elif rtype == TYPE_AREA:
327  key = 'area'
328  elif rtype == TYPE_ISLE:
329  key = 'isle'
330  elif rtype == TYPE_DIRECTION:
331  key = 'direction'
332 
333  if key not in ('direction', 'area', 'isle'):
334  self.topology[key] += 1
335 
336  if key in ('area', 'isle'):
337  pen = wx.TRANSPARENT_PEN
338  if key == 'area':
339  brush = wx.Brush(self.settings[key]['color'], wx.SOLID)
340  else:
341  brush = wx.TRANSPARENT_BRUSH
342  else:
343  pen = wx.Pen(self.settings[key]['color'], self.settings['lineWidth'], wx.SOLID)
344  brush = None
345 
346  return pen, brush
347 
348  def _getDrawFlag(self):
349  """!Get draw flag from the settings
350 
351  See vedit.h for list of draw flags.
352 
353  @return draw flag (int)
354  """
355  ret = 0
356  if self.settings['point']['enabled']:
357  ret |= DRAW_POINT
358  if self.settings['line']['enabled']:
359  ret |= DRAW_LINE
360  if self.settings['boundaryNo']['enabled']:
361  ret |= DRAW_BOUNDARYNO
362  if self.settings['boundaryTwo']['enabled']:
363  ret |= DRAW_BOUNDARYTWO
364  if self.settings['boundaryOne']['enabled']:
365  ret |= DRAW_BOUNDARYONE
366  if self.settings['centroidIn']['enabled']:
367  ret |= DRAW_CENTROIDIN
368  if self.settings['centroidOut']['enabled']:
369  ret |= DRAW_CENTROIDOUT
370  if self.settings['centroidDup']['enabled']:
371  ret |= DRAW_CENTROIDDUP
372  if self.settings['nodeOne']['enabled']:
373  ret |= DRAW_NODEONE
374  if self.settings['nodeTwo']['enabled']:
375  ret |= DRAW_NODETWO
376  if self.settings['vertex']['enabled']:
377  ret |= DRAW_VERTEX
378  if self.settings['area']['enabled']:
379  ret |= DRAW_AREA
380  if self.settings['direction']['enabled']:
381  ret |= DRAW_DIRECTION
382 
383  return ret
384 
385  def _isSelected(self, line, force = False):
386  """!Check if vector object selected?
387 
388  @param line feature id
389 
390  @return True if vector object is selected
391  @return False if vector object is not selected
392  """
393  if len(self.selected['cats']) < 1 or force:
394  # select by id
395  if line in self.selected['ids']:
396  return True
397  else:
398  # select by cat
399  cats = self.poCats.contents
400  for i in range(cats.n_cats):
401  if cats.field[i] == self.selected['field'] and \
402  cats.cat[i] in self.selected['cats']:
403  # remember id
404  # -> after drawing all features selected.cats is reseted */
405  self.selected['ids'].append(line)
406  return True
407 
408  return False
409 
410  def _isDuplicated(self, line):
411  """!Check for already marked duplicates
412 
413  @param line feature id
414 
415  @return True line already marked as duplicated
416  @return False not duplicated
417  """
418  return line in self.selected['idsDupl']
419 
420  def _getRegionBox(self):
421  """!Get bound_box() from current region
422 
423  @return bound_box
424  """
425  box = bound_box()
426 
427  box.N = self.region['n']
428  box.S = self.region['s']
429  box.E = self.region['e']
430  box.W = self.region['w']
431  box.T = PORT_DOUBLE_MAX
432  box.B = -PORT_DOUBLE_MAX
433 
434  return box
435 
436  def DrawMap(self, force = False):
437  """!Draw content of the vector map to the device
438 
439  @param force force drawing
440  @return number of drawn features
441  @return -1 on error
442  """
443  Debug.msg(1, "DisplayDriver.DrawMap(): force=%d", force)
444 
445  if not self.poMapInfo or not self.dc or not self.dcTmp:
446  return -1
447 
448  rlist = Vedit_render_map(self.poMapInfo, byref(self._getRegionBox()), self._getDrawFlag(),
449  self.region['center_easting'], self.region['center_northing'],
450  self.mapObj.width, self.mapObj.height,
451  max(self.region['nsres'], self.region['ewres'])).contents
452 
453  self._resetTopology()
454 
455  self.dc.BeginDrawing()
456  self.dcTmp.BeginDrawing()
457 
458  # draw objects
459  for i in range(rlist.nitems):
460  robj = rlist.item[i].contents
461  self._drawObject(robj)
462 
463  self.dc.EndDrawing()
464  self.dcTmp.EndDrawing()
465 
466  # reset list of selected features by cat
467  # list of ids - see IsSelected()
468  self.selected['field'] = -1
469  self.selected['cats'] = list()
470 
471  def _getSelectType(self):
472  """!Get type(s) to be selected
473 
474  Used by SelectLinesByBox() and SelectLineByPoint()
475  """
476  ftype = 0
477  for feature in (('point', GV_POINT),
478  ('line', GV_LINE),
479  ('centroid', GV_CENTROID),
480  ('boundary', GV_BOUNDARY)):
481  if UserSettings.Get(group = 'vdigit', key = 'selectType',
482  subkey = [feature[0], 'enabled']):
483  ftype |= feature[1]
484 
485  return ftype
486 
487  def _validLine(self, line):
488  """!Check if feature id is valid
489 
490  @param line feature id
491 
492  @return True valid feature id
493  @return False invalid
494  """
495  if line > 0 and line <= Vect_get_num_lines(self.poMapInfo):
496  return True
497 
498  return False
499 
500  def SelectLinesByBox(self, bbox, drawSeg = False, poMapInfo = None):
501  """!Select vector objects by given bounding box
502 
503  If line id is already in the list of selected lines, then it will
504  be excluded from this list.
505 
506  @param bbox bounding box definition
507  @param drawSeg True to draw segments of line
508  @param poMapInfo use external Map_info, None for self.poMapInfo
509 
510  @return number of selected features
511  @return None on error
512  """
513  thisMapInfo = poMapInfo is None
514  if not poMapInfo:
515  poMapInfo = self.poMapInfo
516 
517  if not poMapInfo:
518  return None
519 
520  if thisMapInfo:
521  self._drawSegments = drawSeg
522  self._drawSelected = True
523 
524  # select by ids
525  self.selected['cats'] = list()
526 
527  poList = Vect_new_list()
528  x1, y1 = bbox[0]
529  x2, y2 = bbox[1]
530  poBbox = Vect_new_line_struct()
531  Vect_append_point(poBbox, x1, y1, 0.0)
532  Vect_append_point(poBbox, x2, y1, 0.0)
533  Vect_append_point(poBbox, x2, y2, 0.0)
534  Vect_append_point(poBbox, x1, y2, 0.0)
535  Vect_append_point(poBbox, x1, y1, 0.0)
536 
537  Vect_select_lines_by_polygon(poMapInfo, poBbox,
538  0, None, # isles
539  self._getSelectType(), poList)
540 
541  flist = poList.contents
542  nlines = flist.n_values
543  Debug.msg(1, "DisplayDriver.SelectLinesByBox() num = %d", nlines)
544  for i in range(nlines):
545  line = flist.value[i]
546  if UserSettings.Get(group = 'vdigit', key = 'selectInside',
547  subkey = 'enabled'):
548  inside = True
549  if not self._validLine(line):
550  return None
551  Vect_read_line(poMapInfo, self.poPoints, None, line)
552  points = self.poPoints.contents
553  for p in range(points.n_points):
554  if not Vect_point_in_poly(points.x[p], points.y[p],
555  poBbox):
556  inside = False
557  break
558 
559  if not inside:
560  continue # skip lines just overlapping bbox
561 
562  if not self._isSelected(line):
563  self.selected['ids'].append(line)
564  else:
565  self.selected['ids'].remove(line)
566 
568  Vect_destroy_list(poList)
569 
570  return nlines
571 
572  def SelectLineByPoint(self, point, poMapInfo = None):
573  """!Select vector feature by given point in given
574  threshold
575 
576  Only one vector object can be selected. Bounding boxes of
577  all segments are stores.
578 
579  @param point points coordinates (x, y)
580  @param poMapInfo use external Map_info, None for self.poMapInfo
581 
582  @return dict {'line' : feature id, 'point' : point on line}
583  """
584  thisMapInfo = poMapInfo is None
585  if not poMapInfo:
586  poMapInfo = self.poMapInfo
587 
588  if not poMapInfo:
589  return { 'line' : -1, 'point': None }
590 
591  if thisMapInfo:
592  self._drawSelected = True
593  # select by ids
594  self.selected['cats'] = list()
595 
596  poFound = Vect_new_list()
597 
598  lineNearest = Vect_find_line_list(poMapInfo, point[0], point[1], 0,
599  self._getSelectType(), self.GetThreshold(), self.is3D,
600  None, poFound)
601  Debug.msg(1, "DisplayDriver.SelectLineByPoint() found = %d", lineNearest)
602 
603  if lineNearest > 0:
604  if not self._isSelected(lineNearest):
605  self.selected['ids'].append(lineNearest)
606  else:
607  self.selected['ids'].remove(lineNearest)
608 
609  px = c_double()
610  py = c_double()
611  pz = c_double()
612  if not self._validLine(lineNearest):
613  return { 'line' : -1, 'point': None }
614  ftype = Vect_read_line(poMapInfo, self.poPoints, self.poCats, lineNearest)
615  Vect_line_distance (self.poPoints, point[0], point[1], 0.0, self.is3D,
616  byref(px), byref(py), byref(pz),
617  None, None, None)
618 
619  # check for duplicates
620  if self.settings['highlightDupl']['enabled']:
621  found = poFound.contents
622  for i in range(found.n_values):
623  line = found.value[i]
624  if line != lineNearest:
625  self.selected['ids'].append(line)
626 
627  self.GetDuplicates()
628 
629  for i in range(found.n_values):
630  line = found.value[i]
631  if line != lineNearest and not self._isDuplicated(line):
632  self.selected['ids'].remove(line)
633 
634  Vect_destroy_list(poFound)
635 
636  if thisMapInfo:
637  # drawing segments can be very expensive
638  # only one features selected
639  self._drawSegments = True
640 
641  return { 'line' : lineNearest,
642  'point' : (px.value, py.value, pz.value) }
643 
644  def _listToIList(self, plist):
645  """!Generate from list struct_ilist
646  """
647  ilist = Vect_new_list()
648  for val in plist:
649  Vect_list_append(ilist, val)
650 
651  return ilist
652 
653  def GetSelectedIList(self, ilist = None):
654  """!Get list of selected objects as struct_ilist
655 
656  Returned IList must be freed by Vect_destroy_list().
657 
658  @return struct_ilist
659  """
660  if ilist:
661  return self._listToIList(ilist)
662 
663  return self._listToIList(self.selected['ids'])
664 
665  def GetSelected(self, grassId = True):
666  """!Get ids of selected objects
667 
668  @param grassId True for feature id, False for PseudoDC id
669 
670  @return list of ids of selected vector objects
671  """
672  if grassId:
673  return self.selected['ids']
674 
675  dc_ids = list()
676 
677  if not self._drawSegments:
678  dc_ids.append(1)
679  elif len(self.selected['ids']) > 0:
680  # only first selected feature
681  Vect_read_line(self.poMapInfo, self.poPoints, None,
682  self.selected['ids'][0])
683  points = self.poPoints.contents
684  # node - segment - vertex - segment - node
685  for i in range(1, 2 * points.n_points):
686  dc_ids.append(i)
687 
688  return dc_ids
689 
690  def SetSelected(self, ids, layer = -1):
691  """!Set selected vector objects
692 
693  @param list of ids (None to unselect features)
694  @param layer layer number for features selected based on category number
695  """
696  if ids:
697  self._drawSelected = True
698  else:
699  self._drawSelected = False
700 
701  if layer > 0:
702  selected.field = layer
703  self.selected['cats'] = ids
704  else:
705  field = -1
706  self.selected['ids'] = ids
707 
708  def GetSelectedVertex(self, pos):
709  """!Get PseudoDC vertex id of selected line
710 
711  Set bounding box for vertices of line.
712 
713  @param pos position
714 
715  @return id of center, left and right vertex
716  @return 0 no line found
717  @return -1 on error
718  """
719  returnId = list()
720  # only one object can be selected
721  if len(self.selected['ids']) != 1 or not self._drawSegments:
722  return returnId
723 
724  startId = 1
725  line = self.selected['ids'][0]
726 
727  if not self._validLine(line):
728  return -1
729  ftype = Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line)
730 
731  minDist = 0.0
732  Gid = -1
733  # find the closest vertex (x, y)
734  DCid = 1
735  points = self.poPoints.contents
736  for idx in range(points.n_points):
737  dist = Vect_points_distance(pos[0], pos[1], 0.0,
738  points.x[idx], points.y[idx], points.z[idx], 0)
739 
740  if idx == 0:
741  minDist = dist
742  Gid = idx
743  else:
744  if minDist > dist:
745  minDist = dist
746  Gid = idx
747 
748  vx, vy = self._cell2Pixel(points.x[idx], points.y[idx], points.z[idx])
749  rect = wx.Rect(vx, vy, 0, 0)
750  self.dc.SetIdBounds(DCid, rect)
751  DCid += 2
752 
753  if minDist > self.GetThreshold():
754  return returnId
755 
756  # translate id
757  DCid = Gid * 2 + 1
758 
759  # add selected vertex
760  returnId.append(DCid)
761  # left vertex
762  if DCid == startId:
763  returnId.append(-1)
764  else:
765  returnId.append(DCid - 2)
766  # right vertex
767  if DCid == (points.n_points - 1) * 2 + startId:
768  returnId.append(-1)
769  else:
770  returnId.append(DCid + 2)
771 
772  return returnId
773 
774  def DrawSelected(self, flag):
775  """!Draw selected features
776 
777  @param flag True to draw selected features
778  """
779  self._drawSelected = bool(flag)
780 
781  def CloseMap(self):
782  """!Close vector map
783 
784  @return 0 on success
785  @return non-zero on error
786  """
787  ret = 0
788  if self.poMapInfo:
789  # rebuild topology
790  Vect_build_partial(self.poMapInfo, GV_BUILD_NONE)
791  Vect_build(self.poMapInfo)
792 
793  # close map and store topo/cidx
794  ret = Vect_close(self.poMapInfo)
795  del self.mapInfo
796  self.poMapInfo = self.mapInfo = None
797 
798  return ret
799 
800  def OpenMap(self, name, mapset, update = True):
801  """!Open vector map by the driver
802 
803  @param name name of vector map to be open
804  @param mapset name of mapset where the vector map lives
805 
806  @return map_info
807  @return None on error
808  """
809  Debug.msg("DisplayDriver.OpenMap(): name=%s mapset=%s updated=%d",
810  name, mapset, update)
811  if not self.mapInfo:
812  self.mapInfo = Map_info()
813  self.poMapInfo = pointer(self.mapInfo)
814 
815  # open existing map
816  if update:
817  ret = Vect_open_update(self.poMapInfo, name, mapset)
818  else:
819  ret = Vect_open_old(self.poMapInfo, name, mapset)
820  self.is3D = Vect_is_3d(self.poMapInfo)
821 
822  if ret == -1: # error
823  del self.mapInfo
824  self.poMapInfo = self.mapInfo = None
825  elif ret < 2:
826  dlg = wx.MessageDialog(parent = self.window,
827  message = _("Topology for vector map <%s> is not available. "
828  "Topology is required by digitizer. Do you want to "
829  "rebuild topology (takes some time) and open the vector map "
830  "for editing?") % name,
831  caption=_("Topology missing"),
832  style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
833  ret = dlg.ShowModal()
834  if ret != wx.ID_YES:
835  del self.mapInfo
836  self.poMapInfo = self.mapInfo = None
837  else:
838  Vect_build(self.poMapInfo)
839 
840  return self.poMapInfo
841 
842  def GetMapBoundingBox(self):
843  """!Get bounding box of (opened) vector map layer
844 
845  @return (w,s,b,e,n,t)
846  """
847  if not self.poMapInfo:
848  return None
849 
850  bbox = bound_box()
851  Vect_get_map_box(self.poMapInfo, byref(bbox))
852 
853  return bbox.W, bbox.S, bbox.B, \
854  bbox.E, bbox.N, bbox.T
855 
856  def UpdateSettings(self, alpha = 255):
857  """!Update display driver settings
858 
859  @todo map units
860 
861  @alpha color value for aplha channel
862  """
863  color = dict()
864  for key in self.settings.keys():
865  if key == 'lineWidth':
866  self.settings[key] = int(UserSettings.Get(group = 'vdigit', key = 'lineWidth',
867  subkey = 'value'))
868  continue
869 
870  color = wx.Color(UserSettings.Get(group = 'vdigit', key = 'symbol',
871  subkey = [key, 'color'])[0],
872  UserSettings.Get(group = 'vdigit', key = 'symbol',
873  subkey = [key, 'color'])[1],
874  UserSettings.Get(group = 'vdigit', key = 'symbol',
875  subkey = [key, 'color'])[2],
876  alpha)
877 
878  if key == 'highlight':
879  self.settings[key] = color
880  continue
881 
882  if key == 'highlightDupl':
883  self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'checkForDupl',
884  subkey = 'enabled'))
885  else:
886  self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'symbol',
887  subkey = [key, 'enabled']))
888 
889  self.settings[key]['color'] = color
890 
891  def UpdateRegion(self):
892  """!Update geographical region used by display driver
893  """
894  self.region = self.mapObj.GetCurrentRegion()
895 
896  def GetThreshold(self, type = 'snapping', value = None, units = None):
897  """!Return threshold value in map units
898 
899  @param type snapping mode (node, vertex)
900  @param value threshold to be set up
901  @param units units (map, screen)
902 
903  @return threshold value
904  """
905  if value is None:
906  value = UserSettings.Get(group = 'vdigit', key = type, subkey = 'value')
907 
908  if units is None:
909  units = UserSettings.Get(group = 'vdigit', key = type, subkey = 'units')
910 
911  if value < 0:
912  value = (self.region['nsres'] + self.region['ewres']) / 2.0
913 
914  if units == "screen pixels":
915  # pixel -> cell
916  res = max(self.region['nsres'], self.region['ewres'])
917  return value * res
918 
919  return value
920 
921  def GetDuplicates(self):
922  """!Return ids of (selected) duplicated vector features
923  """
924  if not self.poMapInfo:
925  return
926 
927  ids = dict()
928  APoints = Vect_new_line_struct()
929  BPoints = Vect_new_line_struct()
930 
931  self.selected['idsDupl'] = list()
932 
933  for i in range(len(self.selected['ids'])):
934  line1 = self.selected['ids'][i]
935  if self._isDuplicated(line1):
936  continue
937 
938  Vect_read_line(self.poMapInfo, APoints, None, line1)
939 
940  for line2 in self.selected['ids']:
941  if line1 == line2 or self._isDuplicated(line2):
942  continue
943 
944  Vect_read_line(self.poMapInfo, BPoints, None, line2)
945 
946  if Vect_line_check_duplicate(APoints, BPoints, WITHOUT_Z):
947  if i not in ids:
948  ids[i] = list()
949  ids[i].append((line1, self._getCatString(line1)))
950  self.selected['idsDupl'].append(line1)
951 
952  ids[i].append((line2, self._getCatString(line2)))
953  self.selected['idsDupl'].append(line2)
954 
955  Vect_destroy_line_struct(APoints)
956  Vect_destroy_line_struct(BPoints)
957 
958  return ids
959 
960  def _getCatString(self, line):
961  Vect_read_line(self.poMapInfo, None, self.poCats, line)
962 
963  cats = self.poCats.contents
964  catsDict = dict()
965  for i in range(cats.n_cats):
966  layer = cats.field[i]
967  if layer not in catsDict:
968  catsDict[layer] = list()
969  catsDict[layer].append(cats.cat[i])
970 
971  catsStr = ''
972  for l, c in catsDict.iteritems():
973  catsStr = '%d: (%s)' % (l, ','.join(map(str, c)))
974 
975  return catsStr
976 
977  def UnSelect(self, lines):
978  """!Unselect vector features
979 
980  @param lines list of feature id(s)
981  """
982  checkForDupl = False
983 
984  for line in lines:
985  if self._isSelected(line):
986  self.selected['ids'].remove(line)
987  if self.settings['highlightDupl']['enabled'] and self._isDuplicated(line):
988  checkForDupl = True
989 
990  if checkForDupl:
991  self.GetDuplicates()
992 
993  return len(self.selected['ids'])
994 
995