/[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 246 by bh, Mon Jul 29 13:38:04 2002 UTC revision 1385 by jonathan, Thu Jul 10 14:52:39 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
   
   
 #  
 #   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)  
   
     def MouseMove(self, event):  
         if self.dragging:  
             self.drag_move(event.m_x, event.m_y)  
   
     def MouseUp(self, event):  
         if self.dragging:  
             self.drag_move(event.m_x, event.m_y)  
   
     def Cancel(self):  
         self.dragging = 0  
   
46    
47  class RectTool(Tool):  class CanvasPanTool(PanTool):
48    
49      """Base class for tools that draw rectangles while dragging"""      """The Canvas Pan Tool"""
   
     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 = (cx, 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"  
50    
51      def MouseMove(self, event):      def MouseMove(self, event):
52          if self.dragging:          if self.dragging:
53              Tool.MouseMove(self, event)              PanTool.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 = OutputTransform(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)
113    
114          # the map displayed in this canvas. Set with SetMap()          self.SetBackgroundColour(wxColour(255, 255, 255))
         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  
   
         # The region to update when idle  
         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)  
   
         # keep track of which layers/shapes are selected to make sure we  
         # only redraw when necessary  
         self.last_selected_layer = None  
         self.last_selected_shape = None  
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 302  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.
# Line 320  class MapCanvas(wxWindow, Publisher): Line 175  class MapCanvas(wxWindow, Publisher):
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.SetBackground(self.backgroundColor)
179              dc.Clear()              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            # This causes a paint event that then draws the bitmap
220            self.redraw()
221    
222        def Export(self):
223    
224            if hasattr(self, "export_path"):
225                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 = OutputTransform(self.scale,
236                                                           self.offset,
237                                                           self.GetSizeTuple(),
238                                                           dc.GetSizeTuple())
239    
240          # blit the bitmap to the screen              selected_layer = self.selection.SelectedLayer()
241          dc = wx.wxMemoryDC()              selected_shapes = self.selection.SelectedShapes()
         dc.SelectObject(bitmap)  
         clientdc = wxClientDC(self)  
         clientdc.BeginDrawing()  
         clientdc.Blit(0, 0, width, height, dc, 0, 0)  
         clientdc.EndDrawing()  
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            selected_shapes = self.selection.SelectedShapes()
263            
264            printout = MapPrintout(self, self.Map(), (0, 0, width, height),
265                                   selected_layer, selected_shapes)
266            printer.Print(self, printout, True)
267          printout.Destroy()          printout.Destroy()
268    
     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.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  
   
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 587  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          """Redraw the map.          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
333            # The selection object takes care that it only issues
334          Receiver for the SELECTED_SHAPE messages. Try to redraw only          # SHAPES_SELECTED messages when the set of selected shapes has
335          when necessary.          # actually changed, so we can do a full redraw unconditionally.
336          """          # FIXME: We should perhaps try to limit the redraw to the are
337          # A redraw is necessary when the display has to change, which          # actually covered by the shapes before and after the selection
338          # means that either the status changes from having no selection          # change.
339          # to having a selection shape or vice versa, or when the fact          ViewPort.shape_selected(self, layer, shape)
340          # whether there is a selection at all doesn't change, when the          self.full_redraw()
         # shape which is selected has changed (which means that layer or  
         # shapeid changes).  
         if ((shape is not None or self.last_selected_shape is not None)  
             and (shape != self.last_selected_shape  
                  or layer != self.last_selected_layer)):  
             self.full_redraw()  
   
         # remember the selection so we can compare when it changes again.  
         self.last_selected_layer = layer  
         self.last_selected_shape = shape  
   
     def unprojected_rect_around_point(self, x, y):  
         """return a rect a few pixels around (x, y) in unprojected corrdinates  
   
         The return value is a tuple (minx, miny, maxx, maxy) suitable a  
         parameter to a layer's ShapesInRegion method.  
         """  
         map_proj = self.map.projection  
         if map_proj is not None:  
             inverse = map_proj.Inverse  
         else:  
             inverse = None  
   
         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, searched_layer = None):  
         """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 searched_layer is given (or not None  
         which it defaults to), only search in that layer.  
         """  
         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 searched_layer:  
             layers = [searched_layer]  
         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 = None):  
         """\  
         Select and return the shape and its layer at window position (x, y)  
   
         If layer is given, only search in that layer. If no layer is  
         given, search through all layers.  
341    
342          Return a tuple (layer, shapeid). If no shape is found, return      def GetTextExtent(self, text):
343          (None, None).          dc = wxClientDC(self)
344            font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
345            dc.SetFont(font)
346            return dc.GetTextExtent(text)
347    
348        def LabelShapeAt(self, x, y, text=None):
349            """Add or remove a label at window position x, y.
350                                                                                    
351            If there's a label at the given position, remove it. Otherwise
352            determine the shape at the position, run the label dialog and
353            unless the user cancels the dialog, add a label.
354          """          """
         layer, shape = result = self.find_shape_at(x, y, searched_layer=layer)  
         # 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)  
         return result  
   
     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)
364                      map_proj = proj          
365                  else:  def OutputTransform(canvas_scale, canvas_offset, canvas_size, device_extend):
366                      map_proj = None      """Calculate dimensions to transform canvas content to output device."""
367                  proj = layer.projection      width, height = device_extend
368                  if proj is not None:  
369                      layer_proj = proj      # Only 80 % of the with are available for the map
370                  else:      width = width * 0.8
371                      layer_proj = None  
372        # Define the distance of the map from DC border
373                  shapetype = layer.ShapeType()      distance = 20
374                  if shapetype == SHAPETYPE_POLYGON:  
375                      x, y = shape_centroid(layer.shapefile.cobject(),      if height < width:
376                                            shape_index,          # landscape
377                                            map_proj, layer_proj, 1, 1, 0, 0)          map_height = height - 2*distance
378                      if map_proj is not None:          map_width = map_height
379                          x, y = map_proj.Inverse(x, y)      else:
380                  else:          # portrait, recalibrate width (usually the legend width is too
381                      shape = layer.Shape(shape_index)          # small
382                      if shapetype == SHAPETYPE_POINT:          width = width * 0.9
383                          x, y = shape.Points()[0]          map_height = width - 2*distance
384                      else:          map_width = map_height
385                          # assume SHAPETYPE_ARC      
386                          points = shape.Points()      mapregion = (distance, distance,
387                          x, y = points[len(points) / 2]                   distance+map_width, distance+map_height)
388                      if layer_proj is not None:  
389                          x, y = layer_proj.Inverse(x, y)      canvas_width, canvas_height = canvas_size
390                  if shapetype == SHAPETYPE_POINT:      
391                      halign = ALIGN_LEFT      scalex = map_width / (canvas_width/canvas_scale)
392                      valign = ALIGN_CENTER      scaley = map_height / (canvas_height/canvas_scale)
393                  elif shapetype == SHAPETYPE_POLYGON:      scale = min(scalex, scaley)
394                      halign = ALIGN_CENTER      canvas_offx, canvas_offy = canvas_offset
395                      valign = ALIGN_CENTER      offx = scale*canvas_offx/canvas_scale
396                  elif shapetype == SHAPETYPE_ARC:      offy = scale*canvas_offy/canvas_scale
397                      halign = ALIGN_LEFT  
398                      valign = ALIGN_CENTER      return scale, (offx, offy), mapregion
                 label_layer.AddLabel(x, y, text,  
                                      halign = halign, valign = valign)  

Legend:
Removed from v.246  
changed lines
  Added in v.1385

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26