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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26