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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26