/[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 159 by bh, Wed May 8 13:46:15 2002 UTC revision 1454 by bh, Fri Jul 18 14:41:04 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002 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 11  Classes for display of a map and interac Line 12  Classes for display of a map and interac
12    
13  __version__ = "$Revision$"  __version__ = "$Revision$"
14    
15    from Thuban import _
16    
17    import sys
18    import os.path
19    
20  from math import hypot  from math import hypot
21    
22  from wxPython.wx import wxWindow,\  from wxPython.wx import wxWindow, \
23       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
24       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \
25         wxBITMAP_TYPE_XPM, wxCursor, wxPlatform, \
26         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 wxproj import point_in_polygon_shape, shape_centroid
36    
37    from Thuban.Model.messages import \
38         MAP_PROJECTION_CHANGED, MAP_LAYERS_CHANGED, \
39         LAYER_PROJECTION_CHANGED, LAYER_CHANGED, LAYER_VISIBILITY_CHANGED
40    
41  from Thuban.Model.messages import MAP_PROJECTION_CHANGED, \  from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
      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  
 from Thuban.Lib.connector import Publisher  
   
 from renderer import ScreenRenderer, PrinterRender  
42    
43  import labeldialog  import labeldialog
44    
45  from messages import SELECTED_SHAPE, VIEW_POSITION  from viewport import ViewPort, PanTool, output_transform
   
46    
47  #  class CanvasPanTool(PanTool):
 #   The tools  
 #  
   
 class Tool:  
   
     """  
     Base class for the interactive tools  
     """  
48    
49      def __init__(self, view):      """The Canvas Pan Tool"""
         """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)  
             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:  
             Tool.MouseMove(self, event)  
54              sx, sy = self.start              sx, sy = self.start
55              x, y = self.current              x, y = self.current
56              width, height = self.view.GetSizeTuple()              width, height = self.view.GetSizeTuple()
# Line 178  class PanTool(Tool): Line 61  class PanTool(Tool):
61              dc = self.view.drag_dc              dc = self.view.drag_dc
62              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
63    
     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)  
   
   
   
   
64  class MapPrintout(wx.wxPrintout):  class MapPrintout(wx.wxPrintout):
65    
66      """      """
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 231  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)          renderer = PrinterRenderer(dc, scale, offset, resolution = resy)
96          renderer.RenderMap(self.map)          x, y, width, height = self.region
97          return wx.true          canvas_scale = self.canvas.scale
98                    renderer.RenderMap(self.map,
99                               (0,0,
100                                    (width/canvas_scale)*scale,
101                                    (height/canvas_scale)*scale),
102                                    mapregion,
103                               self.selected_layer, self.selected_shapes)
104            return True
105    
106  class MapCanvas(wxWindow, Publisher):  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          self.SetBackgroundColour(wxColour(255, 255, 255))          ViewPort.__init__(self)
   
         # the map displayed in this canvas. Set with SetMap()  
         self.map = None  
   
         # scale and offset describe the transformation from projected  
         # coordinates to window coordinates.  
         self.scale = 1.0  
         self.offset = (0, 0)  
   
         # whether the user is currently dragging the mouse, i.e. moving  
         # the mouse while pressing a mouse button  
         self.dragging = 0  
   
         # the currently active tool  
         self.tool = None  
   
         # The current mouse position of the last OnMotion event or None  
         # if the mouse is outside the window.  
         self.current_position = None  
   
         # If true, OnIdle will call do_redraw to do the actual  
         # redrawing. Set by OnPaint to avoid some unnecessary redraws.  
         # To force a redraw call full_redraw().  
         self.redraw_on_idle = 0  
113    
114          # The region to update when idle          self.SetBackgroundColour(wxColour(255, 255, 255))
         self.update_region = wx.wxRegion()  
115    
116          # the bitmap serving as backing store          # the bitmap serving as backing store
117          self.bitmap = None          self.bitmap = None
118    
119          # the interactor          self.backgroundColor = wx.wxWHITE_BRUSH
         self.interactor = interactor  
         self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)  
120    
121          # subscribe the WX events we're interested in          # subscribe the WX events we're interested in
122          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
# Line 297  class MapCanvas(wxWindow, Publisher): Line 129  class MapCanvas(wxWindow, Publisher):
129    
130      def __del__(self):      def __del__(self):
131          wxWindow.__del__(self)          wxWindow.__del__(self)
132          Publisher.__del__(self)          ViewPort.__del__(self)
133    
134        def PanTool(self):
135            """Start the canvas pan tool"""
136            self.SelectTool(CanvasPanTool(self))
137            
138        def SetMap(self, map):
139            redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
140                               LAYER_VISIBILITY_CHANGED)
141            if self.Map() is not None:
142                for channel in redraw_channels:
143                    self.Map().Unsubscribe(channel, self.full_redraw)
144    
145            ViewPort.SetMap(self, map)
146    
147            if self.Map() is not None:
148                for channel in redraw_channels:
149                    self.Map().Subscribe(channel, self.full_redraw)
150    
151            # force a redraw. If map is not empty, it's already been called
152            # by FitMapToWindow but if map is empty it hasn't been called
153            # yet so we have to explicitly call it.
154            self.full_redraw()
155    
156      def OnPaint(self, event):      def OnPaint(self, event):
157          dc = wxPaintDC(self)          dc = wxPaintDC(self)
158          if self.map is not None and self.map.HasLayers():  
159              # We have a non-empty map. Redraw it in idle time          if self.Map() is not None and self.Map().HasLayers():
160              self.redraw_on_idle = 1              if self.bitmap in (None, -1):
161              # update the region that has to be redrawn                  # set the flag that we should redraw the
162              self.update_region.UnionRegion(self.GetUpdateRegion())                  # bitmap in idle time
163                    self.bitmap = -1
164                else:
165                    # blit the bitmap to the screen
166                    dc.BeginDrawing()
167                    dc.DrawBitmap(self.bitmap, 0, 0)
168                    dc.EndDrawing()
169          else:          else:
170              # 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
171              # the screen.              # the screen.
172                
173              # XXX it's probably possible to get rid of this. The              # XXX it's probably possible to get rid of this. The
174              # background color of the window is already white and the              # background color of the window is already white and the
175              # only thing we may have to do is to call self.Refresh()              # only thing we may have to do is to call self.Refresh()
176              # with a true argument in the right places.              # with a true argument in the right places.
177              dc.BeginDrawing()              dc.BeginDrawing()
178              dc.Clear()                          dc.SetBackground(self.backgroundColor)
179                dc.Clear()
180              dc.EndDrawing()              dc.EndDrawing()
181    
182              # clear the region      def OnIdle(self, event):
183              self.update_region = wx.wxRegion()          # render the screen if necessary
   
     def do_redraw(self):  
         # This should only be called if we have a non-empty map.  
184    
185          # get the update region and reset it. We're not actually using          if self.bitmap != -1:
186          # it anymore, though.              return
         update_box = self.update_region.GetBox()  
         self.update_region = wx.wxRegion()  
187    
188          # Get the window size.          wxBeginBusyCursor()
189          width, height = self.GetSizeTuple()          try:
190                width, height = self.GetSizeTuple()
191    
         # If self.bitmap's still there, reuse it. Otherwise redraw it  
         if self.bitmap is not None:  
             bitmap = self.bitmap  
         else:  
192              bitmap = wx.wxEmptyBitmap(width, height)              bitmap = wx.wxEmptyBitmap(width, height)
193              dc = wx.wxMemoryDC()              dc = wx.wxMemoryDC()
194              dc.SelectObject(bitmap)              dc.SelectObject(bitmap)
195              dc.BeginDrawing()              dc.BeginDrawing()
196    
197              # clear the background              dc.SetBackground(self.backgroundColor)
198              dc.SetBrush(wx.wxWHITE_BRUSH)              dc.Clear()
199              dc.SetPen(wx.wxTRANSPARENT_PEN)  
200              dc.DrawRectangle(0, 0, width, height)              selected_layer = self.selection.SelectedLayer()
201                selected_shapes = self.selection.SelectedShapes()
             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  
202    
203              # draw the map into the bitmap              # draw the map into the bitmap
204              renderer = ScreenRenderer(dc, self.scale, self.offset)              renderer = ScreenRenderer(dc, self.scale, self.offset)
205    
206              # Pass the entire bitmap as update_region to the renderer.              # Pass the entire bitmap as update region to the renderer.
207              # We're redrawing the whole bitmap, after all.              # We're redrawing the whole bitmap, after all.
208              renderer.RenderMap(self.map, (0, 0, width, height),              renderer.RenderMap(self.Map(), (0, 0, width, height),
209                                 selected_layer, selected_shape)                                 selected_layer, selected_shapes)
210    
211              dc.EndDrawing()              dc.EndDrawing()
212              dc.SelectObject(wx.wxNullBitmap)              dc.SelectObject(wx.wxNullBitmap)
213    
214              self.bitmap = bitmap              self.bitmap = bitmap
215            finally:
216                wxEndBusyCursor()
217                pass
218    
219          # blit the bitmap to the screen          # This causes a paint event that then draws the bitmap
220          dc = wx.wxMemoryDC()          self.redraw()
221          dc.SelectObject(bitmap)  
222          clientdc = wxClientDC(self)      def Export(self):
223          clientdc.BeginDrawing()  
224          clientdc.Blit(0, 0, width, height, dc, 0, 0)          if hasattr(self, "export_path"):
225          clientdc.EndDrawing()              export_path = self.export_path
226            else:
227                export_path="."
228            dlg = wxFileDialog(self, _("Export Map"), export_path, "",
229                               "Enhanced Metafile (*.wmf)|*.wmf",
230                               wxSAVE|wxOVERWRITE_PROMPT)
231            if dlg.ShowModal() == wxID_OK:
232                self.export_path = os.path.dirname(dlg.GetPath())
233                dc = wxMetaFileDC(dlg.GetPath())
234        
235                scale, offset, mapregion = output_transform(self.scale,
236                                                            self.offset,
237                                                            self.GetSizeTuple(),
238                                                            dc.GetSizeTuple())
239    
240                selected_layer = self.selection.SelectedLayer()
241                selected_shapes = self.selection.SelectedShapes()
242    
243                renderer = ExportRenderer(dc, scale, offset)
244    
245                # Pass the entire bitmap as update region to the renderer.
246                # We're redrawing the whole bitmap, after all.
247                width, height = self.GetSizeTuple()
248                renderer.RenderMap(self.Map(),
249                                    (0,0,
250                                        (width/self.scale)*scale,
251                                        (height/self.scale)*scale),
252                                    mapregion,
253                                    selected_layer, selected_shapes)
254                dc.EndDrawing()
255                dc.Close()
256            dlg.Destroy()
257            
258      def Print(self):      def Print(self):
259          printer = wx.wxPrinter()          printer = wx.wxPrinter()
260          printout = MapPrintout(self.map)          width, height = self.GetSizeTuple()
261          printer.Print(self, printout, wx.true)          selected_layer = self.selection.SelectedLayer()
262          printout.Destroy()          selected_shapes = self.selection.SelectedShapes()
263                    
264      def SetMap(self, map):          printout = MapPrintout(self, self.Map(), (0, 0, width, height),
265          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,                                 selected_layer, selected_shapes)
266                             LAYER_VISIBILITY_CHANGED)          printer.Print(self, printout, True)
267          if self.map is not None:          printout.Destroy()
             for channel in redraw_channels:  
                 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.full_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.full_redraw()  
   
     def Map(self):  
         return self.map  
268    
269      def redraw(self, *args):      def redraw(self, *args):
270          self.Refresh(0)          self.Refresh(False)
271    
272      def full_redraw(self, *args):      def full_redraw(self, *args):
273          self.bitmap = None          self.bitmap = None
274          self.redraw()          self.redraw()
275    
276      def projection_changed(self, *args):      def map_projection_changed(self, map, old_proj):
277          self.FitMapToWindow()          ViewPort.map_projection_changed(self, map, old_proj)
278          self.full_redraw()          self.full_redraw()
279    
280      def set_view_transform(self, scale, offset):      def layer_projection_changed(self, *args):
281          self.scale = scale          ViewPort.layer_projection_changed(self, args)
         self.offset = offset  
282          self.full_redraw()          self.full_redraw()
283    
284      def proj_to_win(self, x, y):      def set_view_transform(self, scale, offset):
285          """\          ViewPort.set_view_transform(self, scale, offset)
286          Return the point in  window coords given by projected coordinates x y          self.full_redraw()
         """  
         offx, offy = self.offset  
         return (self.scale * x + offx, -self.scale * y + offy)  
   
     def win_to_proj(self, x, y):  
         """\  
         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)  
   
     def FitRectToWindow(self, rect):  
         width, height = self.GetSizeTuple()  
         llx, lly, urx, ury = rect  
         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)  
   
     def ZoomOutToRect(self, rect):  
         # rect is given in window coordinates  
   
         # determine the bbox of the displayed region in projected  
         # coordinates  
         width, height = self.GetSizeTuple()  
         llx, lly = self.win_to_proj(0, height - 1)  
         urx, ury = self.win_to_proj(width - 1, 0)  
   
         sx, sy, ex, ey = rect  
         scalex = (ex - sx) / (urx - llx)  
         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))  
   
     def ZoomInTool(self):  
         self.tool = ZoomInTool(self)  
   
     def ZoomOutTool(self):  
         self.tool = ZoomOutTool(self)  
   
     def PanTool(self):  
         self.tool = PanTool(self)  
   
     def IdentifyTool(self):  
         self.tool = IdentifyTool(self)  
   
     def LabelTool(self):  
         self.tool = LabelTool(self)  
   
     def CurrentTool(self):  
         return self.tool and self.tool.Name() or None  
   
     def CurrentPosition(self):  
         """Return current position of the mouse in projected coordinates.  
   
         The result is a 2-tuple of floats with the coordinates. If the  
         mouse is not in the window, the result is None.  
         """  
         if self.current_position is not None:  
             x, y = self.current_position  
             return self.win_to_proj(x, y)  
         else:  
             return None  
   
     def set_current_position(self, event):  
         """Set the current position from event  
287    
288          Should be called by all events that contain mouse positions      def GetPortSizeTuple(self):
289          especially EVT_MOTION. The event paramete may be None to          return self.GetSizeTuple()
         indicate the the pointer left the window.  
         """  
         if event is not None:  
             self.current_position = (event.m_x, event.m_y)  
         else:  
             self.current_position = None  
         self.issue(VIEW_POSITION)  
290    
291      def OnLeftDown(self, event):      def OnLeftDown(self, event):
292          self.set_current_position(event)          self.MouseLeftDown(event)
293          if self.tool is not None:          if self.tool is not None:
294              self.drag_dc = wxClientDC(self)              self.drag_dc = wxClientDC(self)
295              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wxINVERT)
296              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)              self.drag_dc.SetBrush(wxTRANSPARENT_BRUSH)
             self.CaptureMouse()  
             self.tool.MouseDown(event)  
297              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
298              self.dragging = 1              self.dragging = 1
299            
300      def OnLeftUp(self, event):      def OnLeftUp(self, event):
301          self.ReleaseMouse()          self.MouseLeftUp(event)
         self.set_current_position(event)  
302          if self.dragging:          if self.dragging:
303              self.tool.Hide(self.drag_dc)              self.ReleaseMouse()
304              self.tool.MouseUp(event)              try:
305              self.drag_dc = None                  self.tool.Hide(self.drag_dc)
306          self.dragging = 0              finally:
307                    self.drag_dc = None
308                    self.dragging = 0
309    
310      def OnMotion(self, event):      def OnMotion(self, event):
         self.set_current_position(event)  
311          if self.dragging:          if self.dragging:
312              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
313              self.tool.MouseMove(event)  
314            self.MouseMove(event)
315    
316            if self.dragging:
317              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
318    
319      def OnLeaveWindow(self, event):      def OnLeaveWindow(self, event):
320          self.set_current_position(None)          self.set_current_position(None)
321    
     def OnIdle(self, event):  
         if self.redraw_on_idle:  
             self.do_redraw()  
         self.redraw_on_idle = 0  
   
322      def OnSize(self, event):      def OnSize(self, event):
323          # the window's size has changed. We have to get a new bitmap. If          # the window's size has changed. We have to get a new bitmap. If
324          # we want to be clever we could try to get by without throwing          # we want to be clever we could try to get by without throwing
# Line 582  class MapCanvas(wxWindow, Publisher): Line 329  class MapCanvas(wxWindow, Publisher):
329          self.full_redraw()          self.full_redraw()
330    
331      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
332            """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
333            # The selection object takes care that it only issues
334            # SHAPES_SELECTED messages when the set of selected shapes has
335            # actually changed, so we can do a full redraw unconditionally.
336            # FIXME: We should perhaps try to limit the redraw to the are
337            # actually covered by the shapes before and after the selection
338            # change.
339            ViewPort.shape_selected(self, layer, shape)
340          self.full_redraw()          self.full_redraw()
341    
342      def unprojected_rect_around_point(self, x, y):      def GetTextExtent(self, text):
343          """return a rect a few pixels around (x, y) in unprojected corrdinates          dc = wxClientDC(self)
344            font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
345          The return value is a tuple (minx, miny, maxx, maxy) suitable a          dc.SetFont(font)
346          parameter to a layer's ShapesInRegion method.          return dc.GetTextExtent(text)
347          """  
348          map_proj = self.map.projection      def LabelShapeAt(self, x, y, text=None):
349          if map_proj is not None:          """Add or remove a label at window position x, y.
350              inverse = map_proj.Inverse  
351          else:          If there's a label at the given position, remove it. Otherwise
352              inverse = None          determine the shape at the position, run the label dialog and
353            unless the user cancels the dialog, add a label.
         xs = []  
         ys = []  
         for dx, dy in ((-1, -1), (1, -1), (1, 1), (-1, 1)):  
             px, py = self.win_to_proj(x + dx, y + dy)  
             if inverse:  
                 px, py = inverse(px, py)  
             xs.append(px)  
             ys.append(py)  
         return (min(xs), min(ys), max(xs), max(ys))  
   
     def find_shape_at(self, px, py, select_labels = 0, selected_layer = 1):  
         """Determine the shape at point px, py in window coords  
   
         Return the shape and the corresponding layer as a tuple (layer,  
         shape).  
   
         If the optional parameter select_labels is true (default false)  
         search through the labels. If a label is found return it's index  
         as the shape and None as the layer.  
   
         If the optional parameter selected_layer is true (default), only  
         search in the currently selected layer.  
354          """          """
         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  
   
         box = self.unprojected_rect_around_point(px, py)  
   
         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) - 1, -1, -1):  
                     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  
   
             shape_ids = layer.ShapesInRegion(box)  
             shape_ids.reverse()  
   
             if shapetype == SHAPETYPE_POLYGON:  
                 for i in shape_ids:  
                     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 shape_ids:  
                     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 shape_ids:  
                     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, selected_layer = 0)  
         # 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  
355          label_layer = self.map.LabelLayer()          label_layer = self.map.LabelLayer()
356          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)
357          if layer is None and shape_index is not None:          if layer is None and shape_index is not None:
358              # a label was selected              ViewPort.LabelShapeAt(self, x, y)
             label_layer.RemoveLabel(shape_index)  
359          elif layer is not None:          elif layer is not None:
360              text = labeldialog.run_label_dialog(self, layer.table, shape_index)              text = labeldialog.run_label_dialog(self,
361              if text:                                                  layer.ShapeStore().Table(),
362                  proj = self.map.projection                                                  shape_index)
363                  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.159  
changed lines
  Added in v.1454

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26