/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/view.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/view.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 6 by bh, Tue Aug 28 15:41:52 2001 UTC revision 2700 by dpinte, Mon Sep 18 14:27:02 2006 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 by Intevation GmbH  # opyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4    # Frank Koormann <[email protected]>
5  #  #
6  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
7  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 9  Line 10 
10  Classes for display of a map and interaction with it  Classes for display of a map and interaction with it
11  """  """
12    
13  __version__ = "$Revision$"  from __future__ import generators
   
 from math import hypot  
   
 from wxPython.wx import wxWindow,\  
      wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\  
      EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION  
14    
15    __version__ = "$Revision$"
16    # $Source$
17    # $Id$
18    
19  from wxPython import wx  import os.path
20    import time
21    import traceback
22    
23  from wxproj import point_in_polygon_shape, shape_centroid  import wx
24    
25    # Export related stuff
26    if wx.Platform == '__WXMSW__':
27        from wx import MetaFileDC
28    
29  from Thuban.Model.messages import MAP_PROJECTION_CHANGED, \  from Thuban import _
      LAYERS_CHANGED, LAYER_LEGEND_CHANGED, LAYER_VISIBILITY_CHANGED  
 from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \  
      SHAPETYPE_POINT  
 from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  
      ALIGN_LEFT, ALIGN_RIGHT  
30    
31    from Thuban.Model.messages import MAP_LAYERS_CHANGED, LAYER_CHANGED, \
32         LAYER_VISIBILITY_CHANGED
33    
34  from renderer import ScreenRenderer, PrinterRender  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
35    
36  import labeldialog  import labeldialog
37    
38  from messages import SELECTED_SHAPE  from viewport import ViewPort, PanTool, output_transform
39    
40    class CanvasPanTool(PanTool):
41    
42  #      """The Canvas Pan Tool"""
 #   The tools  
 #  
   
 class Tool:  
   
     """  
     Base class for the interactive tools  
     """  
   
     def __init__(self, view):  
         """Intitialize the tool. The view is the canvas displaying the map"""  
         self.view = view  
         self.start = self.current = None  
         self.dragging = 0  
         self.drawn = 0  
   
     def Name(self):  
         """Return the tool's name"""  
         return ''  
   
     def drag_start(self, x, y):  
         self.start = self.current = x, y  
         self.dragging = 1  
   
     def drag_move(self, x, y):  
         self.current = x, y  
   
     def drag_stop(self, x, y):  
         self.current = x, y  
         self.dragging = 0  
   
     def Show(self, dc):  
         if not self.drawn:  
             self.draw(dc)  
         self.drawn = 1  
   
     def Hide(self, dc):  
         if self.drawn:  
             self.draw(dc)  
         self.drawn = 0  
   
     def draw(self, dc):  
         pass  
   
     def MouseDown(self, event):  
         self.drag_start(event.m_x, event.m_y)  
43    
44      def MouseMove(self, event):      def MouseMove(self, event):
45          if self.dragging:          if self.dragging:
46              self.drag_move(event.m_x, event.m_y)              PanTool.MouseMove(self, event)
   
     def MouseUp(self, event):  
         if self.dragging:  
             self.drag_move(event.m_x, event.m_y)  
   
     def Cancel(self):  
         self.dragging = 0  
   
   
 class RectTool(Tool):  
   
     """Base class for tools that draw rectangles while dragging"""  
   
     def draw(self, dc):  
         sx, sy = self.start  
         cx, cy = self.current  
         dc.DrawRectangle(sx, sy, cx - sx, cy - sy)  
   
 class ZoomInTool(RectTool):  
   
     """The Zoom-In Tool"""  
   
     def Name(self):  
         return "ZoomInTool"  
   
     def proj_rect(self):  
         """return the rectangle given by start and current in projected  
         coordinates"""  
         sx, sy = self.start  
         cx, cy = self.current  
         left, top = self.view.win_to_proj(sx, sy)  
         right, bottom = self.view.win_to_proj(cx, cy)  
         return (min(left, right), min(top, bottom),  
                 max(left, right), max(top, bottom))  
   
     def MouseUp(self, event):  
         if self.dragging:  
             Tool.MouseUp(self, event)  
             self.view.FitRectToWindow(self.proj_rect())  
   
   
 class ZoomOutTool(RectTool):  
   
     """The Zoom-Out Tool"""  
       
     def Name(self):  
         return "ZoomOutTool"  
   
     def MouseUp(self, event):  
         if self.dragging:  
             Tool.MouseUp(self, event)  
47              sx, sy = self.start              sx, sy = self.start
             cx, cy = self.current  
             self.view.ZoomOutToRect((min(sx, cx), min(sy, cy),  
                                      max(sx, cx), max(sy, cy)))  
   
   
 class PanTool(Tool):  
   
     """The Pan Tool"""  
   
     def Name(self):  
         return "PanTool"  
   
     def MouseMove(self, event):  
         if self.dragging:  
             x0, y0 = self.current  
             Tool.MouseMove(self, event)  
48              x, y = self.current              x, y = self.current
49              width, height = self.view.GetSizeTuple()              width, height = self.view.GetSizeTuple()
             dc = self.view.drag_dc  
             dc.Blit(0, 0, width, height, dc, x0 - x, y0 - y)  
   
     def MouseUp(self, event):  
         if self.dragging:  
             Tool.MouseUp(self, event)  
             sx, sy = self.start  
             cx, cy = self.current  
             self.view.Translate(cx - sx, cy - sy)  
           
 class IdentifyTool(Tool):  
   
     """The "Identify" Tool"""  
       
     def Name(self):  
         return "IdentifyTool"  
   
     def MouseUp(self, event):  
         self.view.SelectShapeAt(event.m_x, event.m_y)  
   
   
 class LabelTool(Tool):  
   
     """The "Label" Tool"""  
   
     def Name(self):  
         return "LabelTool"  
   
     def MouseUp(self, event):  
         self.view.LabelShapeAt(event.m_x, event.m_y)  
   
50    
51                bitmapdc = wx.MemoryDC()
52                bitmapdc.SelectObject(self.view.PreviewBitmap())
53    
54                dc = self.view.drag_dc
55                dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
56    
57  class MapPrintout(wx.wxPrintout):  class MapPrintout(wx.Printout):
58    
59      """      """
60      wxPrintout class for printing Thuban maps      wxPrintout class for printing Thuban maps
61      """      """
62    
63      def __init__(self, map):      def __init__(self, canvas, map, region, selected_layer, selected_shapes):
64          wx.wxPrintout.__init__(self)          wx.Printout.__init__(self)
65            self.canvas = canvas
66          self.map = map          self.map = map
67            self.region = region
68            self.selected_layer = selected_layer
69            self.selected_shapes = selected_shapes
70    
71      def GetPageInfo(self):      def GetPageInfo(self):
72          return (1, 1, 1, 1)          return (1, 1, 1, 1)
# Line 215  class MapPrintout(wx.wxPrintout): Line 80  class MapPrintout(wx.wxPrintout):
80    
81      def draw_on_dc(self, dc):      def draw_on_dc(self, dc):
82          width, height = self.GetPageSizePixels()          width, height = self.GetPageSizePixels()
83          llx, lly, urx, ury = self.map.ProjectedBoundingBox()          scale, offset, mapregion = output_transform(self.canvas.scale,
84          scalex = width / (urx - llx)                                                      self.canvas.offset,
85          scaley = height / (ury - lly)                                                      self.canvas.GetSizeTuple(),
86          scale = min(scalex, scaley)                                                      self.GetPageSizePixels())
         offx = 0.5 * (width - (urx + llx) * scale)  
         offy = 0.5 * (height + (ury + lly) * scale)  
   
87          resx, resy = self.GetPPIPrinter()          resx, resy = self.GetPPIPrinter()
88          renderer = PrinterRender(dc, scale, (offx, offy), resolution = resx)          canvas_scale = self.canvas.scale
89          renderer.RenderMap(self.map)          x, y, width, height = self.region
90          return wx.true          renderer = PrinterRenderer(dc, self.map, scale, offset,
91                                               region = (mapregion[0], mapregion[1],
92                                                 (width/canvas_scale)*scale,
93                                                 (height/canvas_scale)*scale),
94                                       resolution = resy,
95                                       destination_region = mapregion)
96            renderer.RenderMap(self.selected_layer, self.selected_shapes)
97            return True
98    
99    
100  class MapCanvas(wxWindow):  class MapCanvas(wx.Window, ViewPort):
101    
102      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
103    
104      def __init__(self, parent, winid):      def __init__(self, parent, winid):
105          wxWindow.__init__(self, parent, winid)          wx.Window.__init__(self, parent, winid)
106          self.SetBackgroundColour(wxColour(255, 255, 255))          ViewPort.__init__(self)
         self.map = None  
         self.scale = 1.0  
         self.offset = (0, 0)  
         self.dragging = 0  
         self.tool = None  
         self.redraw_on_idle = 0  
         EVT_PAINT(self, self.OnPaint)  
         EVT_LEFT_DOWN(self, self.OnLeftDown)  
         EVT_LEFT_UP(self, self.OnLeftUp)  
         EVT_MOTION(self, self.OnMotion)  
         wx.EVT_IDLE(self, self.OnIdle)  
         import main  
         self.interactor = main.app.interactor  
         self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)  
   
     def OnPaint(self, event):  
         dc = wxPaintDC(self)  
         if self.map is None or not self.map.HasLayers():  
             return  
         self.redraw_on_idle = 1  
   
     def do_redraw(self):  
         width, height = self.GetSizeTuple()  
         bitmap = wx.wxEmptyBitmap(width, height)  
   
         dc = wx.wxMemoryDC()  
         dc.SelectObject(bitmap)  
107    
108          dc.BeginDrawing()          self.SetBackgroundColour(wx.Colour(255, 255, 255))
109    
110          dc.SetBrush(wx.wxWHITE_BRUSH)          # the bitmap serving as backing store
111          dc.SetPen(wx.wxTRANSPARENT_PEN)          self.bitmap = None
112          dc.DrawRectangle(0, 0, width, height)          # the monochrome bitmap with the selection if any
113            self.selection_bitmap = None
114    
115            self.backgroundColor = wx.WHITE_BRUSH
116    
117            # The rendering iterator object. Used when rendering
118            # incrementally
119            self.render_iter = None
120    
121            # subscribe the WX events we're interested in
122            self.Bind(wx.EVT_PAINT, self.OnPaint)
123            self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
124            self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
125            self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
126            self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
127            self.Bind(wx.EVT_MOTION, self.OnMotion)
128            self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
129            self.Bind(wx.EVT_SIZE, self.OnSize)
130            self.Bind(wx.EVT_IDLE, self.OnIdle)
131    
132        def __del__(self):
133            wx.Window.__del__(self)
134            ViewPort.__del__(self)
135    
136          if 1: #self.interactor.selected_map is self.map:      def PreviewBitmap(self):
137              selected_layer = self.interactor.selected_layer          return self.bitmap
             selected_shape = self.interactor.selected_shape  
         else:  
             selected_layer = None  
             selected_shape = None  
               
         renderer = ScreenRenderer(dc, self.scale, self.offset)  
         renderer.RenderMap(self.map, selected_layer, selected_shape)  
   
         clientdc = wxClientDC(self)  
         clientdc.BeginDrawing()  
         clientdc.Blit(0, 0, width, height, dc, 0, 0)  
         clientdc.EndDrawing()  
138    
139        def PanTool(self):
140            """Start the canvas pan tool"""
141            self.SelectTool(CanvasPanTool(self))
142    
     def Print(self):  
         printer = wx.wxPrinter()  
         printout = MapPrintout(self.map)  
         printer.Print(self, printout, wx.true)  
         printout.Destroy()  
           
143      def SetMap(self, map):      def SetMap(self, map):
144          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,          redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
145                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
146          if self.map is not None:          if self.Map() is not None:
147              for channel in redraw_channels:              for channel in redraw_channels:
148                  self.map.Unsubscribe(channel, self.redraw)                  self.Map().Unsubscribe(channel, self.full_redraw)
             self.map.Unsubscribe(MAP_PROJECTION_CHANGED,  
                                  self.projection_changed)  
         self.map = map  
         if self.map is not None:  
             for channel in redraw_channels:  
                 self.map.Subscribe(channel, self.redraw)  
             self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)  
         self.FitMapToWindow()  
149    
150      def Map(self):          ViewPort.SetMap(self, map)
         return self.map  
151    
152      def redraw(self, *args):          if self.Map() is not None:
153          self.Refresh(0)              for channel in redraw_channels:
154                    self.Map().Subscribe(channel, self.full_redraw)
155    
156      def projection_changed(self, *args):          # force a redraw. If map is not empty, it's already been called
157          self.FitMapToWindow()          # by FitMapToWindow but if map is empty it hasn't been called
158          self.redraw()          # yet so we have to explicitly call it.
159            self.full_redraw()
160    
161      def set_view_transform(self, scale, offset):      def OnPaint(self, event):
162          self.scale = scale          dc = wx.PaintDC(self)
163          self.offset = offset          if self.Map() is not None and self.Map().HasLayers():
164          self.redraw()              if self.bitmap is not None:
165                    dc.BeginDrawing()
166                    dc.DrawBitmap(self.bitmap, 0, 0)
167                    if self.selection_bitmap is not None:
168                        dc.DrawBitmap(self.selection_bitmap, 0, 0, True)
169                    dc.EndDrawing()
170            else:
171                # If we've got no map or if the map is empty, simply clear
172                # the screen.
173    
174      def proj_to_win(self, x, y):              # XXX it's probably possible to get rid of this. The
175          """\              # background color of the window is already white and the
176          Return the point in  window coords given by projected coordinates x y              # only thing we may have to do is to call self.Refresh()
177          """              # with a true argument in the right places.
178          offx, offy = self.offset              dc.BeginDrawing()
179          return (self.scale * x + offx, -self.scale * y + offy)              dc.SetBackground(self.backgroundColor)
180                dc.Clear()
181                dc.EndDrawing()
182    
183      def win_to_proj(self, x, y):      def OnIdle(self, event):
184          """\          """Idle handler. Redraw the bitmap if necessary"""
185          Return the point in projected coordinates given by window coords x y          if (self.Map() is not None
186                and (self.bitmap is None
187                     or self.render_iter is not None
188                     or (self.HasSelectedShapes()
189                         and self.selection_bitmap is None))):
190                event.RequestMore(self._do_redraw())
191    
192        def _do_redraw(self):
193            """Redraw a bit and return whether this method has to be called again.
194    
195            Called by OnIdle to handle the actual redraw. Redraw is
196            incremental for both the bitmap with the normal layers and the
197            bitmap with the selection.
198          """          """
199          offx, offy = self.offset          finished = False
200          return ((x - offx) / self.scale, (offy - y) / self.scale)          if self.render_iter is not None:
201                try:
202                    if self.render_iter.next():
203                        # Redraw if the last preview redraw was some time
204                        # ago and the user is not currently dragging the
205                        # mouse because redrawing would interfere with what
206                        # the current tool is drawing on the window.
207                        if not self.dragging \
208                               and time.time() - self.render_last_preview > 0.5:
209                            client_dc = wx.ClientDC(self)
210                            client_dc.BeginDrawing()
211                            client_dc.DrawBitmap(self.bitmap, 0, 0)
212                            client_dc.EndDrawing()
213                            self.render_last_preview = time.time()
214                    else:
215                        self.render_iter = None
216                        # Redraw if not dragging because redrawing would
217                        # interfere with what the current tool is drawing on
218                        # the window.
219                        if not self.dragging:
220                            self.redraw()
221                        finished = True
222                except StopIteration:
223                    finished = True
224                    self.render_iter = None
225                except:
226                    finished = True
227                    self.render_iter = None
228                    traceback.print_exc()
229            else:
230                self.render_iter = self._render_iterator()
231                self.render_last_preview = time.time()
232            return not finished
233    
234      def FitRectToWindow(self, rect):      def _render_iterator(self):
235          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
236          llx, lly, urx, ury = rect          dc = wx.MemoryDC()
         scalex = width / (urx - llx)  
         scaley = height / (ury - lly)  
         scale = min(scalex, scaley)  
         offx = 0.5 * (width - (urx + llx) * scale)  
         offy = 0.5 * (height + (ury + lly) * scale)  
         self.set_view_transform(scale, (offx, offy))  
   
     def FitMapToWindow(self):  
         """\  
         Set the scale and offset so that the map is centered in the  
         window  
         """  
         bbox = self.map.ProjectedBoundingBox()  
         if bbox is not None:  
             self.FitRectToWindow(bbox)  
237    
238      def ZoomFactor(self, factor):          render_start = time.time()
239          width, height = self.GetSizeTuple()  
240          scale = self.scale * factor          if self.bitmap is None:
241          offx, offy = self.offset              self.bitmap = wx.EmptyBitmap(width, height)
242          offset = (factor * (offx - width / 2) + width / 2,              dc.SelectObject(self.bitmap)
243                    factor * (offy - height / 2) + height / 2)              dc.BeginDrawing()
244          self.set_view_transform(scale, offset)  
245                dc.SetBackground(self.backgroundColor)
246                dc.Clear()
247    
248                # draw the map into the bitmap
249                renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
250                                          (0, 0, width, height))
251                for cont in renderer.RenderMapIncrementally():
252                    yield True
253    
254                dc.EndDrawing()
255                dc.SelectObject(wx.NullBitmap)
256    
257            if self.HasSelectedShapes() and self.selection_bitmap is None:
258                bitmap = wx.EmptyBitmap(width, height)
259                dc.SelectObject(bitmap)
260                dc.BeginDrawing()
261                dc.SetBackground(wx.WHITE_BRUSH)
262                dc.Clear()
263    
264                renderer = ScreenRenderer(dc, self.Map(), self.scale, self.offset,
265                                          (0, 0, width, height))
266                layer = self.SelectedLayer()
267                shapes = self.selection.SelectedShapes()
268                for cont in renderer.draw_selection_incrementally(layer, shapes):
269                    yield True
270    
271                dc.EndDrawing()
272                dc.SelectObject(wx.NullBitmap)
273    
274                bitmap.SetMask(wx.Mask(bitmap, wx.WHITE))
275                self.selection_bitmap = bitmap
276    
277            yield False
278    
279        def Export(self):
280    
281      def ZoomOutToRect(self, rect):          if hasattr(self, "export_path"):
282          # rect is given in window coordinates              export_path = self.export_path
283            else:
284                export_path="."
285            dlg = wx.FileDialog(self, _("Export Map"), export_path, "",
286                               "Enhanced Metafile (*.wmf)|*.wmf",
287                               wx.SAVE|wx.OVERWRITE_PROMPT)
288            if dlg.ShowModal() == wx.ID_OK:
289                self.export_path = os.path.dirname(dlg.GetPath())
290                dc = wx.MetaFileDC(dlg.GetPath())
291    
292                scale, offset, mapregion = output_transform(self.scale,
293                                                            self.offset,
294                                                            self.GetSizeTuple(),
295                                                            dc.GetSizeTuple())
296    
297                selected_layer = self.selection.SelectedLayer()
298                selected_shapes = self.selection.SelectedShapes()
299    
300                width, height = self.GetSizeTuple()
301                renderer = ExportRenderer(dc, self.Map(), scale, offset,
302                                          region = (0, 0,
303                                                    (width/self.scale)*scale,
304                                                    (height/self.scale)*scale),
305                                          destination_region = mapregion)
306                renderer.RenderMap(selected_layer, selected_shapes)
307    
308                dc.EndDrawing()
309                dc.Close()
310            dlg.Destroy()
311    
312          # determine the bbox of the displayed region in projected      def Print(self):
313          # coordinates          printer = wx.Printer()
314          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
315          llx, lly = self.win_to_proj(0, height - 1)          selected_layer = self.selection.SelectedLayer()
316          urx, ury = self.win_to_proj(width - 1, 0)          selected_shapes = self.selection.SelectedShapes()
317    
318          sx, sy, ex, ey = rect          printout = MapPrintout(self, self.Map(), (0, 0, width, height),
319          scalex = (ex - sx) / (urx - llx)                                 selected_layer, selected_shapes)
320          scaley = (ey - sy) / (ury - lly)          printer.Print(self, printout, True)
321          scale = min(scalex, scaley)          printout.Destroy()
   
         offx = 0.5 * ((ex + sx) - (urx + llx) * scale)  
         offy = 0.5 * ((ey + sy) + (ury + lly) * scale)  
         self.set_view_transform(scale, (offx, offy))  
   
     def Translate(self, dx, dy):  
         offx, offy = self.offset  
         self.set_view_transform(self.scale, (offx + dx, offy + dy))  
322    
323      def ZoomInTool(self):      def redraw(self, *args):
324          self.tool = ZoomInTool(self)          self.Refresh(False)
325    
326      def ZoomOutTool(self):      def full_redraw(self, *args):
327          self.tool = ZoomOutTool(self)          self.bitmap = None
328            self.selection_bitmap = None
329            self.render_iter = None
330            self.redraw()
331    
332      def PanTool(self):      def redraw_selection(self, *args):
333          self.tool = PanTool(self)          self.selection_bitmap = None
334            self.render_iter = None
335            self.redraw()
336    
337      def IdentifyTool(self):      def map_projection_changed(self, map, old_proj):
338          self.tool = IdentifyTool(self)          ViewPort.map_projection_changed(self, map, old_proj)
339            self.full_redraw()
340    
341        def layer_projection_changed(self, *args):
342            ViewPort.layer_projection_changed(self, args)
343            self.full_redraw()
344    
345      def LabelTool(self):      def set_view_transform(self, scale, offset):
346          self.tool = LabelTool(self)          ViewPort.set_view_transform(self, scale, offset)
347            self.full_redraw()
348    
349      def CurrentTool(self):      def GetPortSizeTuple(self):
350          return self.tool and self.tool.Name() or None          return self.GetSizeTuple()
351    
352        def OnMiddleDown(self, event):
353            self.remembertool = self.tool
354            if self.Map() is not None and self.Map().HasLayers():
355                self.PanTool()
356                self.OnLeftDown(event)
357    
358        def OnMiddleUp(self, event):
359            self.OnLeftUp(event)
360            if self.remembertool:
361                self.SelectTool(self.remembertool)
362    
363      def OnLeftDown(self, event):      def OnLeftDown(self, event):
364            self.MouseLeftDown(event)
365          if self.tool is not None:          if self.tool is not None:
366              self.drag_dc = wxClientDC(self)              self.drag_dc = wx.ClientDC(self)
367              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wx.INVERT)
368              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)              self.drag_dc.SetBrush(wx.TRANSPARENT_BRUSH)
             self.CaptureMouse()  
             self.tool.MouseDown(event)  
369              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
370                self.CaptureMouse()
371              self.dragging = 1              self.dragging = 1
372            
373      def OnLeftUp(self, event):      def OnLeftUp(self, event):
374          self.ReleaseMouse()          """Handle EVT_LEFT_UP
375    
376            Release the mouse if it was captured, if a tool is active call
377            its Hide method and call self.MouseLeftUp.
378            """
379            # It's important that ReleaseMouse is called before MouseLeftUp.
380            # MouseLeftUp may pop up modal dialogs which leads to an
381            # effectively frozen X session because the user can only
382            # interact with the dialog but the mouse is still grabbed by the
383            # canvas.
384          if self.dragging:          if self.dragging:
385              self.tool.Hide(self.drag_dc)              if self.HasCapture():
386              self.tool.MouseUp(event)                  self.ReleaseMouse()
387              self.drag_dc = None              try:
388          self.dragging = 0                  self.tool.Hide(self.drag_dc)
389                finally:
390                    self.drag_dc = None
391                    self.dragging = 0
392            self.MouseLeftUp(event)
393    
394      def OnMotion(self, event):      def OnMotion(self, event):
395          if self.dragging:          if self.dragging:
396              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
             self.tool.MouseMove(event)  
             self.tool.Show(self.drag_dc)  
397    
398      def OnIdle(self, event):          self.MouseMove(event)
         if self.redraw_on_idle:  
             self.do_redraw()  
         self.redraw_on_idle = 0  
399    
400      def shape_selected(self, layer, shape):          if self.dragging:
401          self.redraw()              self.tool.Show(self.drag_dc)
   
     def find_shape_at(self, px, py, select_labels = 0):  
         """Return a tuple shape at point px, py in window coords."""  
         map_proj = self.map.projection  
         if map_proj is not None:  
             forward = map_proj.Forward  
         else:  
             forward = None  
402    
403          scale = self.scale      def OnLeaveWindow(self, event):
404          offx, offy = self.offset          self.set_current_position(None)
405    
406          if select_labels:      def OnSize(self, event):
407              labels = self.map.LabelLayer().Labels()          # the window's size has changed. We have to get a new bitmap. If
408                        # we want to be clever we could try to get by without throwing
409              if labels:          # everything away. E.g. when the window gets smaller, we could
410                  dc = wxClientDC(self)          # either keep the bitmap or create the new one from the old one.
411                  font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)          # Even when the window becomes larger some parts of the bitmap
412                  dc.SetFont(font)          # could be reused.
413                  for i in range(len(labels)):          self.full_redraw()
                     label = labels[i]  
                     x = label.x  
                     y = label.y  
                     text = label.text  
                     if forward:  
                         x, y = forward(x, y)  
                     x = x * scale + offx  
                     y = -y * scale + offy  
                     width, height = dc.GetTextExtent(text)  
                     if label.halign == ALIGN_LEFT:  
                         # nothing to be done  
                         pass  
                     elif label.halign == ALIGN_RIGHT:  
                         x = x - width  
                     elif label.halign == ALIGN_CENTER:  
                         x = x - width/2  
                     if label.valign == ALIGN_TOP:  
                         # nothing to be done  
                         pass  
                     elif label.valign == ALIGN_BOTTOM:  
                         y = y - height  
                     elif label.valign == ALIGN_CENTER:  
                         y = y - height/2  
                     if x <= px < x + width and y <= py <= y + height:  
                         return None, i  
                   
         layers = self.map.Layers()  
         for layer_index in range(len(layers) - 1, -1, -1):  
             layer = layers[layer_index]  
   
             # search only in visible layers  
             if not layer.Visible():  
                 continue  
   
             filled = layer.fill is not None  
             stroked = layer.stroke is not None  
                   
             layer_proj = layer.projection  
             if layer_proj is not None:  
                 inverse = layer_proj.Inverse  
             else:  
                 inverse = None  
                   
             shapetype = layer.ShapeType()  
   
             select_shape = -1  
             if shapetype == SHAPETYPE_POLYGON:  
                 for i in range(layer.NumShapes()):  
                     result = point_in_polygon_shape(layer.shapefile.cobject(),  
                                                     i,  
                                                     filled, stroked,  
                                                     map_proj, layer_proj,  
                                                     scale, -scale, offx, offy,  
                                                     px, py)  
                     if result:  
                         select_shape = i  
                         break  
             elif shapetype == SHAPETYPE_ARC:  
                 for i in range(layer.NumShapes()):  
                     result = point_in_polygon_shape(layer.shapefile.cobject(),  
                                                     i, 0, 1,  
                                                     map_proj, layer_proj,  
                                                     scale, -scale, offx, offy,  
                                                     px, py)  
                     if result < 0:  
                         select_shape = i  
                         break  
             elif shapetype == SHAPETYPE_POINT:  
                 for i in range(layer.NumShapes()):  
                     shape = layer.Shape(i)  
                     x, y = shape.Points()[0]  
                     if inverse:  
                         x, y = inverse(x, y)  
                     if forward:  
                         x, y = forward(x, y)  
                     x = x * scale + offx  
                     y = -y * scale + offy  
                     if hypot(px - x, py - y) < 5:  
                         select_shape = i  
                         break  
   
             if select_shape >= 0:  
                 return layer, select_shape  
         return None, None  
   
     def SelectShapeAt(self, x, y):  
         layer, shape = self.find_shape_at(x, y)  
         self.interactor.SelectLayerAndShape(layer, shape)  
414    
415      def LabelShapeAt(self, x, y):      def shape_selected(self, layer, shape):
416          ox = x; oy = y          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
417            # The selection object takes care that it only issues
418            # SHAPES_SELECTED messages when the set of selected shapes has
419            # actually changed, so we can do a full redraw of the
420            # selection_bitmap unconditionally.
421            ViewPort.shape_selected(self, layer, shape)
422            self.redraw_selection()
423    
424        def GetTextExtent(self, text):
425            dc = wx.ClientDC(self)
426            font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)
427            dc.SetFont(font)
428            return dc.GetTextExtent(text)
429    
430        def LabelShapeAt(self, x, y, text=None):
431            """Add or remove a label at window position x, y.
432    
433            If there's a label at the given position, remove it. Otherwise
434            determine the shape at the position, run the label dialog and
435            unless the user cancels the dialog, add a label.
436            """
437          label_layer = self.map.LabelLayer()          label_layer = self.map.LabelLayer()
438          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
439          if layer is None and shape_index is not None:          if layer is None and shape_index is not None:
440              # a label was selected              ViewPort.LabelShapeAt(self, x, y)
             label_layer.RemoveLabel(shape_index)  
441          elif layer is not None:          elif layer is not None:
442              text = labeldialog.run_label_dialog(self, layer.table, shape_index)              text = labeldialog.run_label_dialog(self,
443              if text:                                                  layer.ShapeStore().Table(),
444                  proj = self.map.projection                                                  shape_index)
445                  if proj is not None:              ViewPort.LabelShapeAt(self, x, y, text)
                     map_proj = proj  
                 else:  
                     map_proj = None  
                 proj = layer.projection  
                 if proj is not None:  
                     layer_proj = proj  
                 else:  
                     layer_proj = None  
   
                 shapetype = layer.ShapeType()  
                 if shapetype == SHAPETYPE_POLYGON:  
                     x, y = shape_centroid(layer.shapefile.cobject(),  
                                           shape_index,  
                                           map_proj, layer_proj, 1, 1, 0, 0)  
                     if map_proj is not None:  
                         x, y = map_proj.Inverse(x, y)  
                 else:  
                     shape = layer.Shape(shape_index)  
                     if shapetype == SHAPETYPE_POINT:  
                         x, y = shape.Points()[0]  
                     else:  
                         # assume SHAPETYPE_ARC  
                         points = shape.Points()  
                         x, y = points[len(points) / 2]  
                     if layer_proj is not None:  
                         x, y = layer_proj.Inverse(x, y)  
                 if shapetype == SHAPETYPE_POINT:  
                     halign = ALIGN_LEFT  
                     valign = ALIGN_CENTER  
                 elif shapetype == SHAPETYPE_POLYGON:  
                     halign = ALIGN_CENTER  
                     valign = ALIGN_CENTER  
                 elif shapetype == SHAPETYPE_ARC:  
                     halign = ALIGN_LEFT  
                     valign = ALIGN_CENTER  
                 label_layer.AddLabel(x, y, text,  
                                      halign = halign, valign = valign)  

Legend:
Removed from v.6  
changed lines
  Added in v.2700

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26