1"""
2@package mapwin.graphics
3
4@brief Map display canvas - buffered window.
5
6Classes:
7 - graphics::GraphicsSet
8 - graphics::GraphicsSetItem
9
10(C) 2006-2013 by the GRASS Development Team
11
12This program is free software under the GNU General Public License
13(>=v2). Read the file COPYING that comes with GRASS for details.
14
15@author Stepan Turek <stepan.turek seznam.cz> (handlers support, GraphicsSet)
16"""
17
18
19from copy import copy
20
21import wx
22
23from gui_core.wrap import NewId
24
25
26class GraphicsSet:
27
28    def __init__(self, parentMapWin, graphicsType, pdc,
29                 setStatusFunc=None, drawFunc=None, mapCoords=True):
30        """Class, which contains instances of GraphicsSetItem and
31            draws them For description of parameters look at method
32            RegisterGraphicsToDraw in BufferedWindow class.
33        """
34        self.pens = {
35            "default": wx.Pen(colour=wx.BLACK, width=2, style=wx.SOLID),
36            "selected": wx.Pen(colour=wx.GREEN, width=2, style=wx.SOLID),
37            "unused": wx.Pen(colour=wx.LIGHT_GREY, width=2, style=wx.SOLID),
38            "highest": wx.Pen(colour=wx.RED, width=2, style=wx.SOLID)
39        }
40        self.brushes = {
41            'default': wx.TRANSPARENT_BRUSH
42        }
43
44        # list contains instances of GraphicsSetItem
45        self.itemsList = []
46
47        self.properties = {}
48        self.graphicsType = graphicsType
49        self.parentMapWin = parentMapWin
50        self.setStatusFunc = setStatusFunc
51        self.mapCoords = mapCoords
52        self.pdc = pdc
53
54        if drawFunc:
55            self.drawFunc = drawFunc
56
57        elif self.graphicsType == "point":
58            self.properties["size"] = 5
59
60            self.properties["text"] = {}
61            self.properties["text"]['font'] = wx.Font(
62                pointSize=self.properties["size"],
63                family=wx.FONTFAMILY_DEFAULT,
64                style=wx.FONTSTYLE_NORMAL,
65                weight=wx.FONTWEIGHT_NORMAL)
66            self.properties["text"]['active'] = True
67
68            self.drawFunc = self.parentMapWin.DrawCross
69
70        elif self.graphicsType == "line":
71            self.drawFunc = self.parentMapWin.DrawPolylines
72
73        elif self.graphicsType == "rectangle":
74            self.drawFunc = self.parentMapWin.DrawRectangle
75
76        elif self.graphicsType == "polygon":
77            self.drawFunc = self.parentMapWin.DrawPolygon
78
79    def Draw(self):
80        """Draws all containing items."""
81        itemOrderNum = 0
82        for item in self.itemsList:
83            self._clearId(item.GetId())
84            if self.setStatusFunc is not None:
85                self.setStatusFunc(item, itemOrderNum)
86
87            if item.GetPropertyVal("hide") is True:
88                itemOrderNum += 1
89                continue
90
91            if self.graphicsType == "point":
92                if item.GetPropertyVal("penName"):
93                    self.parentMapWin.pen = self.pens[
94                        item.GetPropertyVal("penName")]
95                else:
96                    self.parentMapWin.pen = self.pens["default"]
97
98                if self.mapCoords:
99                    coords = self.parentMapWin.Cell2Pixel(item.GetCoords())
100                else:
101                    coords = item.GetCoords()
102                size = self.properties["size"]
103
104                label = item.GetPropertyVal("label")
105                if label is None:
106                    self.properties["text"] = None
107                else:
108                    self.properties["text"]['coords'] = [coords[0] + size,
109                                                         coords[1] + size,
110                                                         size, size]
111                    self.properties["text"][
112                        'color'] = self.parentMapWin.pen.GetColour()
113                    self.properties["text"]['text'] = label
114
115                self.drawFunc(pdc=self.pdc, drawid=item.GetId(),
116                              coords=coords,
117                              text=self.properties["text"],
118                              size=self.properties["size"])
119
120            elif self.graphicsType == "line":
121                if item.GetPropertyVal("penName"):
122                    pen = self.pens[item.GetPropertyVal("penName")]
123                else:
124                    pen = self.pens["default"]
125
126                if self.mapCoords:
127                    coords = [
128                        self.parentMapWin.Cell2Pixel(coords)
129                        for coords in item.GetCoords()]
130                else:
131                    coords = item.GetCoords()
132
133                self.drawFunc(pdc=self.pdc, pen=pen,
134                              coords=coords, drawid=item.GetId())
135
136            elif self.graphicsType == "rectangle":
137                if item.GetPropertyVal("penName"):
138                    pen = self.pens[item.GetPropertyVal("penName")]
139                else:
140                    pen = self.pens["default"]
141                if item.GetPropertyVal("brushName"):
142                    brush = self.brushes[item.GetPropertyVal("brushName")]
143                else:
144                    brush = self.brushes["default"]
145                if self.mapCoords:
146                    coords = [
147                        self.parentMapWin.Cell2Pixel(coords)
148                        for coords in item.GetCoords()]
149                else:
150                    coords = item.GetCoords()
151
152                self.drawFunc(
153                    pdc=self.pdc,
154                    pen=pen,
155                    brush=brush,
156                    drawid=item.GetId(),
157                    point1=coords[0],
158                    point2=coords[1])
159
160            elif self.graphicsType == "polygon":
161                if item.GetPropertyVal("penName"):
162                    pen = self.pens[item.GetPropertyVal("penName")]
163                else:
164                    pen = self.pens["default"]
165                if item.GetPropertyVal("brushName"):
166                    brush = self.brushes[item.GetPropertyVal("brushName")]
167                else:
168                    brush = self.brushes["default"]
169                if self.mapCoords:
170                    coords = [
171                        self.parentMapWin.Cell2Pixel(coords)
172                        for coords in item.GetCoords()]
173                else:
174                    coords = item.GetCoords()
175
176                self.drawFunc(pdc=self.pdc, pen=pen, brush=brush,
177                              coords=coords, drawid=item.GetId())
178            itemOrderNum += 1
179
180    def AddItem(self, coords, penName=None, label=None, hide=False):
181        """Append item to the list.
182
183        Added item is put to the last place in drawing order.
184        Could be 'point' or 'line' according to graphicsType.
185
186        :param coords: list of east, north coordinates (double) of item.
187                       Example:
188
189                           * point: [1023, 122]
190                           * line: [[10, 12],[20,40],[23, 2334]]
191                           * rectangle: [[10, 12], [33, 45]]
192        :param penName: the 'default' pen is used if is not defined
193        :type penName: str
194        :param label: label, which will be drawn with point. It is
195                      relavant just for 'point' type.
196        :type label: str
197        :param hide: if it is True, the item is not drawn when self.Draw
198                     is called. Hidden items are also counted in drawing
199                     order.
200        :type hide: bool
201        :return: (GraphicsSetItem) - added item reference
202        """
203        item = GraphicsSetItem(coords=coords, penName=penName, label=label,
204                               hide=hide)
205        self.itemsList.append(item)
206
207        return item
208
209    def DeleteItem(self, item):
210        """Deletes item
211
212        :param item: (GraphicsSetItem) - item to remove
213
214        :return: True if item was removed
215        :return: False if item was not found
216        """
217        try:
218            self.itemsList.remove(item)
219        except ValueError:
220            return False
221
222        return True
223
224    def GetAllItems(self):
225        """Returns list of all containing instances of GraphicsSetItem,
226        in order as they are drawn. If you want to change order of
227        drawing use: SetItemDrawOrder method.
228        """
229        # user can edit objects but not order in list, that is reason,
230        # why is returned shallow copy of data list it should be used
231        # SetItemDrawOrder for changing order
232        return copy(self.itemsList)
233
234    def GetItem(self, drawNum):
235        """Get given item from the list.
236
237        :param drawNum: drawing order (index) number of item
238        :type drawNum: int
239
240        :return: instance of GraphicsSetItem which is drawn in drawNum order
241        :return: False if drawNum was out of range
242        """
243        return self.itemsList[drawNum]
244
245    def SetPropertyVal(self, propName, propVal):
246        """Set property value
247
248        :param propName: - property name: "size", "text"
249                         - both properties are relevant for "point" type
250        :type propName: str
251        :param propVal: property value to be set
252
253        :return: True if value was set
254        :return: False if propName is not "size" or "text" or type is "line"
255        """
256        if propName in self.properties:
257            self.properties[propName] = propVal
258            return True
259
260        return False
261
262    def GetPropertyVal(self, propName):
263        """Get property value
264
265        Raises KeyError if propName is not "size" or "text" or type is
266        "line"
267
268        :param propName: property name: "size", "text" both properties
269               are relevant for "point" type
270        :type propName: str
271
272        :return: value of property
273        """
274        if propName in self.properties:
275            return self.properties[propName]
276
277        raise KeyError(_("Property does not exist: %s") % (propName))
278
279    def AddPen(self, penName, pen):
280        """Add pen
281
282        :param penName: name of added pen
283        :type penName: str
284        :param pen: added pen
285        :type pen: Wx.Pen
286
287        :return: True - if pen was added
288        :return: False - if pen already exists
289        """
290        if penName in self.pens:
291            return False
292
293        self.pens[penName] = pen
294        return True
295
296    def GetPen(self, penName):
297        """Get existing pen
298
299        :param penName: name of pen
300        :type penName: str
301
302        :return: wx.Pen reference if is found
303        :return: None if penName was not found
304        """
305        if penName in self.pens:
306            return self.pens[penName]
307
308        return None
309
310    def AddBrush(self, brushName, brush):
311        """Add brush
312
313        :param brushName: name of added brush
314        :type brushName: str
315        :param brush: added brush
316        :type brush: wx.Brush
317
318        :return: True - if brush was added
319        :return: False - if brush already exists
320        """
321        if brushName in self.brushes:
322            return False
323
324        self.brushes[brushName] = brush
325        return True
326
327    def GetBrush(self, brushName):
328        """Get existing brush
329
330        :param brushName: name of brush
331        :type brushName: str
332
333        :return: wx.Brush reference if is found
334        :return: None if brushName was not found
335        """
336        if brushName in self.brushes:
337            return self.brushes[brushName]
338
339        return None
340
341    def SetItemDrawOrder(self, item, drawNum):
342        """Set draw order for item
343
344        :param item: (GraphicsSetItem)
345        :param drawNum: drawing order of item to be set
346        :type drawNum: int
347
348        :return: True if order was changed
349        :return: False if drawNum is out of range or item was not found
350        """
351        if drawNum < len(self.itemsList) and drawNum >= 0 and \
352                item in self.itemsList:
353            self.itemsList.insert(
354                drawNum, self.itemsList.pop(
355                    self.itemsList.index(item)))
356            return True
357
358        return False
359
360    def GetItemDrawOrder(self, item):
361        """Get draw order for given item
362
363        :param item: (GraphicsSetItem)
364
365        :return: (int) - drawing order of item
366        :return: None - if item was not found
367        """
368        try:
369            return self.itemsList.index(item)
370        except ValueError:
371            return None
372
373    def _clearId(self, drawid):
374        """Clears old object before drawing new object."""
375        try:
376            self.pdc.ClearId(drawid)
377        except:
378            pass
379
380
381class GraphicsSetItem:
382
383    def __init__(self, coords, penName=None,
384                 brushName=None, label=None, hide=False):
385        """Could be point or line according to graphicsType in
386        GraphicsSet class
387
388        :param coords: list of coordinates (double) of item
389                       Example: point: [1023, 122]
390                                line: [[10, 12],[20,40],[23, 2334]]
391                                rectangle: [[10, 12], [33, 45]]
392        :param penName: if it is not defined 'default' pen is used
393        :type penName: str
394        :param brushName: if it is not defined 'default' brush is used
395        :type brushName: str
396        :param label: label, which will be drawn with point. It is
397                      relevant just for 'point' type
398        :type label: str
399        :param hide: if it is True, item is not drawn Hidden items are
400                     also counted in drawing order in GraphicsSet class.
401        :type hide: bool
402        """
403        self.coords = coords
404
405        self.properties = {"penName": penName,
406                           "brushName": brushName,
407                           "hide": hide,
408                           "label": label}
409        self.id = NewId()
410
411    def AddProperty(self, propName):
412        """Adds new property, to set it, call SetPropertyVal afterwards.
413
414        :param propName - name of the newly defined property
415        :type propName: str
416        """
417        if not propName in self.properties:
418            self.properties[propName] = None
419
420    def SetPropertyVal(self, propName, propVal):
421        """Set property value
422
423        :param propName: - property name: "penName", "hide" or "label"
424                         - property "label" is relevant just for 'point' type
425                         - or newly defined property name
426        :type propName: str
427        :param propVal: property value to be set
428
429        :return: True if value was set
430        :return: False if propName is not "penName", "hide" or "label"
431        """
432        if propName in self.properties:
433            self.properties[propName] = propVal
434            return True
435
436        return False
437
438    def GetPropertyVal(self, propName):
439        """Get property value
440
441        Raises KeyError if propName is not "penName", "hide" or
442        "label".
443
444        :param propName: - property name: "penName", "hide" or "label"
445                         - property "label" is relevant just for 'point' type
446        :type propName: str
447
448        :return: value of property
449        """
450        if propName in self.properties:
451            return self.properties[propName]
452
453        raise KeyError(_("Property does not exist: %s") % (propName))
454
455    def SetCoords(self, coords):
456        """Set coordinates of item
457
458        :param coords: list of east, north coordinates (double) of item
459                       Example:
460
461                           * point: [1023, 122]
462                           * line: [[10, 12],[20,40],[23, 2334]]
463                           * rectangle: [[10, 12], [33, 45]]
464        """
465        self.coords = coords
466
467    def GetCoords(self):
468        """Get item coordinates
469
470        :return: coordinates
471        """
472        return self.coords
473
474    def GetId(self):
475        """Get item id (drawing id).
476        """
477        return self.id
478