/[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 60 by bh, Thu Sep 13 14:47:39 2001 UTC revision 295 by bh, Fri Aug 30 10:39:17 2002 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 by Intevation GmbH  # Copyright (c) 2001, 2002 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  #  #
# Line 15  from math import hypot Line 15  from math import hypot
15    
16  from wxPython.wx import wxWindow,\  from wxPython.wx import wxWindow,\
17       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
18       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW
19    
20    
21  from wxPython import wx  from wxPython import wx
# Line 29  from Thuban.Model.layer import SHAPETYPE Line 29  from Thuban.Model.layer import SHAPETYPE
29       SHAPETYPE_POINT       SHAPETYPE_POINT
30  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
31       ALIGN_LEFT, ALIGN_RIGHT       ALIGN_LEFT, ALIGN_RIGHT
32    from Thuban.Lib.connector import Publisher
33    
34  from renderer import ScreenRenderer, PrinterRender  from renderer import ScreenRenderer, PrinterRender
35    
36  import labeldialog  import labeldialog
37    
38  from messages import SELECTED_SHAPE  from messages import SELECTED_SHAPE, VIEW_POSITION
39    
40    
41  #  #
# Line 129  class ZoomInTool(RectTool): Line 129  class ZoomInTool(RectTool):
129              Tool.MouseUp(self, event)              Tool.MouseUp(self, event)
130              sx, sy = self.start              sx, sy = self.start
131              cx, cy = self.current              cx, cy = self.current
132              if sx == cx and sy == cy:              if sx == cx or sy == cy:
133                  # Just a mouse click. Simply zoom in by a factor of two                  # Just a mouse click or a degenerate rectangle. Simply
134                    # zoom in by a factor of two
135                    # FIXME: For a click this is the desired behavior but should we
136                    # really do this for degenrate rectagles as well or
137                    # should we ignore them?
138                  self.view.ZoomFactor(2, center = (cx, cy))                  self.view.ZoomFactor(2, center = (cx, cy))
139              else:              else:
140                  # A drag. Zoom in to the rectangle                  # A drag. Zoom in to the rectangle
# Line 140  class ZoomInTool(RectTool): Line 144  class ZoomInTool(RectTool):
144  class ZoomOutTool(RectTool):  class ZoomOutTool(RectTool):
145    
146      """The Zoom-Out Tool"""      """The Zoom-Out Tool"""
147        
148      def Name(self):      def Name(self):
149          return "ZoomOutTool"          return "ZoomOutTool"
150    
# Line 149  class ZoomOutTool(RectTool): Line 153  class ZoomOutTool(RectTool):
153              Tool.MouseUp(self, event)              Tool.MouseUp(self, event)
154              sx, sy = self.start              sx, sy = self.start
155              cx, cy = self.current              cx, cy = self.current
156              if sx == cx and sy == cy:              if sx == cx or sy == cy:
157                  # Just a mouse click. Simply zoom out by a factor of two                  # Just a mouse click or a degenerate rectangle. Simply
158                  self.view.ZoomFactor(0.5, center = (cy, cy))                  # zoom out by a factor of two.
159                    # FIXME: For a click this is the desired behavior but should we
160                    # really do this for degenrate rectagles as well or
161                    # should we ignore them?
162                    self.view.ZoomFactor(0.5, center = (cx, cy))
163              else:              else:
164                  # A drag. Zoom out to the rectangle                  # A drag. Zoom out to the rectangle
165                  self.view.ZoomOutToRect((min(sx, cx), min(sy, cy),                  self.view.ZoomOutToRect((min(sx, cx), min(sy, cy),
# Line 167  class PanTool(Tool): Line 175  class PanTool(Tool):
175    
176      def MouseMove(self, event):      def MouseMove(self, event):
177          if self.dragging:          if self.dragging:
             x0, y0 = self.current  
178              Tool.MouseMove(self, event)              Tool.MouseMove(self, event)
179                sx, sy = self.start
180              x, y = self.current              x, y = self.current
181              width, height = self.view.GetSizeTuple()              width, height = self.view.GetSizeTuple()
182    
183                bitmapdc = wx.wxMemoryDC()
184                bitmapdc.SelectObject(self.view.bitmap)
185    
186              dc = self.view.drag_dc              dc = self.view.drag_dc
187              dc.Blit(0, 0, width, height, dc, x0 - x, y0 - y)              dc.Blit(0, 0, width, height, bitmapdc, sx - x, sy - y)
188    
189      def MouseUp(self, event):      def MouseUp(self, event):
190          if self.dragging:          if self.dragging:
# Line 180  class PanTool(Tool): Line 192  class PanTool(Tool):
192              sx, sy = self.start              sx, sy = self.start
193              cx, cy = self.current              cx, cy = self.current
194              self.view.Translate(cx - sx, cy - sy)              self.view.Translate(cx - sx, cy - sy)
195            
196  class IdentifyTool(Tool):  class IdentifyTool(Tool):
197    
198      """The "Identify" Tool"""      """The "Identify" Tool"""
199        
200      def Name(self):      def Name(self):
201          return "IdentifyTool"          return "IdentifyTool"
202    
# Line 238  class MapPrintout(wx.wxPrintout): Line 250  class MapPrintout(wx.wxPrintout):
250          renderer = PrinterRender(dc, scale, (offx, offy), resolution = resx)          renderer = PrinterRender(dc, scale, (offx, offy), resolution = resx)
251          renderer.RenderMap(self.map)          renderer.RenderMap(self.map)
252          return wx.true          return wx.true
           
253    
254  class MapCanvas(wxWindow):  
255    class MapCanvas(wxWindow, Publisher):
256    
257      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
258    
259      def __init__(self, parent, winid, interactor):      def __init__(self, parent, winid, interactor):
260          wxWindow.__init__(self, parent, winid)          wxWindow.__init__(self, parent, winid)
261          self.SetBackgroundColour(wxColour(255, 255, 255))          self.SetBackgroundColour(wxColour(255, 255, 255))
262    
263            # the map displayed in this canvas. Set with SetMap()
264          self.map = None          self.map = None
265    
266            # scale and offset describe the transformation from projected
267            # coordinates to window coordinates.
268          self.scale = 1.0          self.scale = 1.0
269          self.offset = (0, 0)          self.offset = (0, 0)
270    
271            # whether the user is currently dragging the mouse, i.e. moving
272            # the mouse while pressing a mouse button
273          self.dragging = 0          self.dragging = 0
274    
275            # the currently active tool
276          self.tool = None          self.tool = None
277    
278            # The current mouse position of the last OnMotion event or None
279            # if the mouse is outside the window.
280            self.current_position = None
281    
282            # If true, OnIdle will call do_redraw to do the actual
283            # redrawing. Set by OnPaint to avoid some unnecessary redraws.
284            # To force a redraw call full_redraw().
285          self.redraw_on_idle = 0          self.redraw_on_idle = 0
286    
287            # The region to update when idle
288            self.update_region = wx.wxRegion()
289    
290            # the bitmap serving as backing store
291            self.bitmap = None
292    
293            # the interactor
294            self.interactor = interactor
295            self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)
296    
297            # keep track of which layers/shapes are selected to make sure we
298            # only redraw when necessary
299            self.last_selected_layer = None
300            self.last_selected_shape = None
301    
302            # subscribe the WX events we're interested in
303          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
304          EVT_LEFT_DOWN(self, self.OnLeftDown)          EVT_LEFT_DOWN(self, self.OnLeftDown)
305          EVT_LEFT_UP(self, self.OnLeftUp)          EVT_LEFT_UP(self, self.OnLeftUp)
306          EVT_MOTION(self, self.OnMotion)          EVT_MOTION(self, self.OnMotion)
307            EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
308            wx.EVT_SIZE(self, self.OnSize)
309          wx.EVT_IDLE(self, self.OnIdle)          wx.EVT_IDLE(self, self.OnIdle)
310          self.interactor = interactor  
311          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)      def __del__(self):
312            wxWindow.__del__(self)
313            Publisher.__del__(self)
314    
315      def OnPaint(self, event):      def OnPaint(self, event):
316          dc = wxPaintDC(self)          dc = wxPaintDC(self)
317          if self.map is not None and self.map.HasLayers():          if self.map is not None and self.map.HasLayers():
318              # We have a non-empty map. Redraw it in idle time              # We have a non-empty map. Redraw it in idle time
319              self.redraw_on_idle = 1              self.redraw_on_idle = 1
320                # update the region that has to be redrawn
321                self.update_region.UnionRegion(self.GetUpdateRegion())
322          else:          else:
323              # 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
324              # the screen.              # the screen.
325                
326              # XXX it's probably possible to get rid of this. The              # XXX it's probably possible to get rid of this. The
327              # background color of the window is already white and the              # background color of the window is already white and the
328              # only thing we may have to do is to call self.Refresh()              # only thing we may have to do is to call self.Refresh()
329              # with a true argument in the right places.              # with a true argument in the right places.
330              dc.BeginDrawing()              dc.BeginDrawing()
331              dc.Clear()                          dc.Clear()
332              dc.EndDrawing()              dc.EndDrawing()
333    
334                # clear the region
335                self.update_region = wx.wxRegion()
336    
337      def do_redraw(self):      def do_redraw(self):
338          # This should only be called if we have a non-empty map. We draw          # This should only be called if we have a non-empty map.
339          # it into a memory DC and then blit it to the screen.  
340            # get the update region and reset it. We're not actually using
341            # it anymore, though.
342            update_box = self.update_region.GetBox()
343            self.update_region = wx.wxRegion()
344    
345            # Get the window size.
346          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
         bitmap = wx.wxEmptyBitmap(width, height)  
         dc = wx.wxMemoryDC()  
         dc.SelectObject(bitmap)  
         dc.BeginDrawing()  
347    
348          # clear the background          # If self.bitmap's still there, reuse it. Otherwise redraw it
349          dc.SetBrush(wx.wxWHITE_BRUSH)          if self.bitmap is not None:
350          dc.SetPen(wx.wxTRANSPARENT_PEN)              bitmap = self.bitmap
         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  
351          else:          else:
352              selected_layer = None              bitmap = wx.wxEmptyBitmap(width, height)
353              selected_shape = None              dc = wx.wxMemoryDC()
354                dc.SelectObject(bitmap)
355                dc.BeginDrawing()
356    
357          # draw the map into the bitmap              # clear the background
358          renderer = ScreenRenderer(dc, self.scale, self.offset)              dc.SetBrush(wx.wxWHITE_BRUSH)
359          renderer.RenderMap(self.map, selected_layer, selected_shape)              dc.SetPen(wx.wxTRANSPARENT_PEN)
360                dc.DrawRectangle(0, 0, width, height)
361    
362                if 1: #self.interactor.selected_map is self.map:
363                    selected_layer = self.interactor.selected_layer
364                    selected_shape = self.interactor.selected_shape
365                else:
366                    selected_layer = None
367                    selected_shape = None
368    
369          dc.EndDrawing()              # draw the map into the bitmap
370                renderer = ScreenRenderer(dc, self.scale, self.offset)
371    
372                # Pass the entire bitmap as update_region to the renderer.
373                # We're redrawing the whole bitmap, after all.
374                renderer.RenderMap(self.map, (0, 0, width, height),
375                                   selected_layer, selected_shape)
376    
377                dc.EndDrawing()
378                dc.SelectObject(wx.wxNullBitmap)
379                self.bitmap = bitmap
380    
381          # blit the bitmap to the screen          # blit the bitmap to the screen
382            dc = wx.wxMemoryDC()
383            dc.SelectObject(bitmap)
384          clientdc = wxClientDC(self)          clientdc = wxClientDC(self)
385          clientdc.BeginDrawing()          clientdc.BeginDrawing()
386          clientdc.Blit(0, 0, width, height, dc, 0, 0)          clientdc.Blit(0, 0, width, height, dc, 0, 0)
# Line 316  class MapCanvas(wxWindow): Line 391  class MapCanvas(wxWindow):
391          printout = MapPrintout(self.map)          printout = MapPrintout(self.map)
392          printer.Print(self, printout, wx.true)          printer.Print(self, printout, wx.true)
393          printout.Destroy()          printout.Destroy()
394            
395      def SetMap(self, map):      def SetMap(self, map):
396          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,
397                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
398          if self.map is not None:          if self.map is not None:
399              for channel in redraw_channels:              for channel in redraw_channels:
400                  self.map.Unsubscribe(channel, self.redraw)                  self.map.Unsubscribe(channel, self.full_redraw)
401              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,
402                                   self.projection_changed)                                   self.projection_changed)
403          self.map = map          self.map = map
404          if self.map is not None:          if self.map is not None:
405              for channel in redraw_channels:              for channel in redraw_channels:
406                  self.map.Subscribe(channel, self.redraw)                  self.map.Subscribe(channel, self.full_redraw)
407              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)
408          self.FitMapToWindow()          self.FitMapToWindow()
409          # force a redraw. If map is not empty, it's already been called          # force a redraw. If map is not empty, it's already been called
410          # by FitMapToWindow but if map is empty it hasn't been called          # by FitMapToWindow but if map is empty it hasn't been called
411          # yet so we have to explicitly call it.          # yet so we have to explicitly call it.
412          self.redraw()          self.full_redraw()
413    
414      def Map(self):      def Map(self):
415            """Return the map displayed by this canvas"""
416          return self.map          return self.map
417    
418      def redraw(self, *args):      def redraw(self, *args):
419          self.Refresh(0)          self.Refresh(0)
420    
421        def full_redraw(self, *args):
422            self.bitmap = None
423            self.redraw()
424    
425      def projection_changed(self, *args):      def projection_changed(self, *args):
426          self.FitMapToWindow()          self.FitMapToWindow()
427          self.redraw()          self.full_redraw()
428    
429      def set_view_transform(self, scale, offset):      def set_view_transform(self, scale, offset):
430          self.scale = scale          self.scale = scale
431          self.offset = offset          self.offset = offset
432          self.redraw()          self.full_redraw()
433    
434      def proj_to_win(self, x, y):      def proj_to_win(self, x, y):
435          """\          """\
# Line 366  class MapCanvas(wxWindow): Line 446  class MapCanvas(wxWindow):
446          return ((x - offx) / self.scale, (offy - y) / self.scale)          return ((x - offx) / self.scale, (offy - y) / self.scale)
447    
448      def FitRectToWindow(self, rect):      def FitRectToWindow(self, rect):
449            """Fit the rectangular region given by rect into the window.
450            
451            Set scale so that rect (in projected coordinates) just fits into
452            the window and center it.
453            """
454          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
455          llx, lly, urx, ury = rect          llx, lly, urx, ury = rect
456          if llx == urx or lly == ury:          if llx == urx or lly == ury:
# Line 379  class MapCanvas(wxWindow): Line 464  class MapCanvas(wxWindow):
464          self.set_view_transform(scale, (offx, offy))          self.set_view_transform(scale, (offx, offy))
465    
466      def FitMapToWindow(self):      def FitMapToWindow(self):
467          """\          """Fit the map to the window
468          Set the scale and offset so that the map is centered in the          
469          window          Set the scale so that the map fits exactly into the window and
470            center it in the window.
471          """          """
472          bbox = self.map.ProjectedBoundingBox()          bbox = self.map.ProjectedBoundingBox()
473          if bbox is not None:          if bbox is not None:
# Line 407  class MapCanvas(wxWindow): Line 493  class MapCanvas(wxWindow):
493          self.set_view_transform(scale, offset)          self.set_view_transform(scale, offset)
494    
495      def ZoomOutToRect(self, rect):      def ZoomOutToRect(self, rect):
496          # rect is given in window coordinates          """Zoom out to fit the currently visible region into rect.
497    
498            The rect parameter is given in window coordinates
499            """
500          # determine the bbox of the displayed region in projected          # determine the bbox of the displayed region in projected
501          # coordinates          # coordinates
502          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
# Line 425  class MapCanvas(wxWindow): Line 513  class MapCanvas(wxWindow):
513          self.set_view_transform(scale, (offx, offy))          self.set_view_transform(scale, (offx, offy))
514    
515      def Translate(self, dx, dy):      def Translate(self, dx, dy):
516            """Move the map by dx, dy pixels"""
517          offx, offy = self.offset          offx, offy = self.offset
518          self.set_view_transform(self.scale, (offx + dx, offy + dy))          self.set_view_transform(self.scale, (offx + dx, offy + dy))
519    
520      def ZoomInTool(self):      def ZoomInTool(self):
521            """Start the zoom in tool"""
522          self.tool = ZoomInTool(self)          self.tool = ZoomInTool(self)
523    
524      def ZoomOutTool(self):      def ZoomOutTool(self):
525            """Start the zoom out tool"""
526          self.tool = ZoomOutTool(self)          self.tool = ZoomOutTool(self)
527    
528      def PanTool(self):      def PanTool(self):
529            """Start the pan tool"""
530          self.tool = PanTool(self)          self.tool = PanTool(self)
531    
532      def IdentifyTool(self):      def IdentifyTool(self):
533            """Start the identify tool"""
534          self.tool = IdentifyTool(self)          self.tool = IdentifyTool(self)
535    
536      def LabelTool(self):      def LabelTool(self):
537            """Start the label tool"""
538          self.tool = LabelTool(self)          self.tool = LabelTool(self)
539    
540      def CurrentTool(self):      def CurrentTool(self):
541            """Return the name of the current tool or None if no tool is active"""
542          return self.tool and self.tool.Name() or None          return self.tool and self.tool.Name() or None
543    
544        def CurrentPosition(self):
545            """Return current position of the mouse in projected coordinates.
546    
547            The result is a 2-tuple of floats with the coordinates. If the
548            mouse is not in the window, the result is None.
549            """
550            if self.current_position is not None:
551                x, y = self.current_position
552                return self.win_to_proj(x, y)
553            else:
554                return None
555    
556        def set_current_position(self, event):
557            """Set the current position from event
558    
559            Should be called by all events that contain mouse positions
560            especially EVT_MOTION. The event paramete may be None to
561            indicate the the pointer left the window.
562            """
563            if event is not None:
564                self.current_position = (event.m_x, event.m_y)
565            else:
566                self.current_position = None
567            self.issue(VIEW_POSITION)
568    
569      def OnLeftDown(self, event):      def OnLeftDown(self, event):
570            self.set_current_position(event)
571          if self.tool is not None:          if self.tool is not None:
572              self.drag_dc = wxClientDC(self)              self.drag_dc = wxClientDC(self)
573              self.drag_dc.SetLogicalFunction(wxINVERT)              self.drag_dc.SetLogicalFunction(wxINVERT)
# Line 455  class MapCanvas(wxWindow): Line 576  class MapCanvas(wxWindow):
576              self.tool.MouseDown(event)              self.tool.MouseDown(event)
577              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
578              self.dragging = 1              self.dragging = 1
579            
580      def OnLeftUp(self, event):      def OnLeftUp(self, event):
581          self.ReleaseMouse()          self.set_current_position(event)
582          if self.dragging:          if self.dragging:
583                self.ReleaseMouse()
584              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
585              self.tool.MouseUp(event)              self.tool.MouseUp(event)
586              self.drag_dc = None              self.drag_dc = None
587          self.dragging = 0          self.dragging = 0
588    
589      def OnMotion(self, event):      def OnMotion(self, event):
590            self.set_current_position(event)
591          if self.dragging:          if self.dragging:
592              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
593              self.tool.MouseMove(event)              self.tool.MouseMove(event)
594              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
595    
596        def OnLeaveWindow(self, event):
597            self.set_current_position(None)
598    
599      def OnIdle(self, event):      def OnIdle(self, event):
600          if self.redraw_on_idle:          if self.redraw_on_idle:
601              self.do_redraw()              self.do_redraw()
602          self.redraw_on_idle = 0          self.redraw_on_idle = 0
603    
604        def OnSize(self, event):
605            # the window's size has changed. We have to get a new bitmap. If
606            # we want to be clever we could try to get by without throwing
607            # everything away. E.g. when the window gets smaller, we could
608            # either keep the bitmap or create the new one from the old one.
609            # Even when the window becomes larger some parts of the bitmap
610            # could be reused.
611            self.full_redraw()
612    
613      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
614          self.redraw()          """Redraw the map.
615    
616            Receiver for the SELECTED_SHAPE messages. Try to redraw only
617            when necessary.
618            """
619            # A redraw is necessary when the display has to change, which
620            # means that either the status changes from having no selection
621            # to having a selection shape or vice versa, or when the fact
622            # whether there is a selection at all doesn't change, when the
623            # shape which is selected has changed (which means that layer or
624            # shapeid changes).
625            if ((shape is not None or self.last_selected_shape is not None)
626                and (shape != self.last_selected_shape
627                     or layer != self.last_selected_layer)):
628                self.full_redraw()
629    
630            # remember the selection so we can compare when it changes again.
631            self.last_selected_layer = layer
632            self.last_selected_shape = shape
633    
634      def find_shape_at(self, px, py, select_labels = 0, selected_layer = 1):      def unprojected_rect_around_point(self, x, y):
635            """return a rect a few pixels around (x, y) in unprojected corrdinates
636    
637            The return value is a tuple (minx, miny, maxx, maxy) suitable a
638            parameter to a layer's ShapesInRegion method.
639            """
640            map_proj = self.map.projection
641            if map_proj is not None:
642                inverse = map_proj.Inverse
643            else:
644                inverse = None
645    
646            xs = []
647            ys = []
648            for dx, dy in ((-1, -1), (1, -1), (1, 1), (-1, 1)):
649                px, py = self.win_to_proj(x + dx, y + dy)
650                if inverse:
651                    px, py = inverse(px, py)
652                xs.append(px)
653                ys.append(py)
654            return (min(xs), min(ys), max(xs), max(ys))
655    
656        def find_shape_at(self, px, py, select_labels = 0, searched_layer = None):
657          """Determine the shape at point px, py in window coords          """Determine the shape at point px, py in window coords
658    
659          Return the shape and the corresponding layer as a tuple (layer,          Return the shape and the corresponding layer as a tuple (layer,
# Line 488  class MapCanvas(wxWindow): Line 663  class MapCanvas(wxWindow):
663          search through the labels. If a label is found return it's index          search through the labels. If a label is found return it's index
664          as the shape and None as the layer.          as the shape and None as the layer.
665    
666          If the optional parameter selected_layer is true (default), only          If the optional parameter searched_layer is given (or not None
667          search in the currently selected layer.          which it defaults to), only search in that layer.
668          """          """
669          map_proj = self.map.projection          map_proj = self.map.projection
670          if map_proj is not None:          if map_proj is not None:
# Line 500  class MapCanvas(wxWindow): Line 675  class MapCanvas(wxWindow):
675          scale = self.scale          scale = self.scale
676          offx, offy = self.offset          offx, offy = self.offset
677    
678            box = self.unprojected_rect_around_point(px, py)
679    
680          if select_labels:          if select_labels:
681              labels = self.map.LabelLayer().Labels()              labels = self.map.LabelLayer().Labels()
682                
683              if labels:              if labels:
684                  dc = wxClientDC(self)                  dc = wxClientDC(self)
685                  font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)                  font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
# Line 534  class MapCanvas(wxWindow): Line 711  class MapCanvas(wxWindow):
711                      if x <= px < x + width and y <= py <= y + height:                      if x <= px < x + width and y <= py <= y + height:
712                          return None, i                          return None, i
713    
714          if selected_layer:          if searched_layer:
715              layer = self.interactor.SelectedLayer()              layers = [searched_layer]
             if layer is not None:  
                 layers = [layer]  
             else:  
                 # no layer selected. Use an empty list to effectively  
                 # ignore all layers.  
                 layers = []  
716          else:          else:
717              layers = self.map.Layers()              layers = self.map.Layers()
718    
# Line 554  class MapCanvas(wxWindow): Line 725  class MapCanvas(wxWindow):
725    
726              filled = layer.fill is not None              filled = layer.fill is not None
727              stroked = layer.stroke is not None              stroked = layer.stroke is not None
728                    
729              layer_proj = layer.projection              layer_proj = layer.projection
730              if layer_proj is not None:              if layer_proj is not None:
731                  inverse = layer_proj.Inverse                  inverse = layer_proj.Inverse
732              else:              else:
733                  inverse = None                  inverse = None
734                    
735              shapetype = layer.ShapeType()              shapetype = layer.ShapeType()
736    
737              select_shape = -1              select_shape = -1
738    
739                shape_ids = layer.ShapesInRegion(box)
740                shape_ids.reverse()
741    
742              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
743                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
744                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
745                                                      i,                                                      i,
746                                                      filled, stroked,                                                      filled, stroked,
# Line 576  class MapCanvas(wxWindow): Line 751  class MapCanvas(wxWindow):
751                          select_shape = i                          select_shape = i
752                          break                          break
753              elif shapetype == SHAPETYPE_ARC:              elif shapetype == SHAPETYPE_ARC:
754                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
755                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
756                                                      i, 0, 1,                                                      i, 0, 1,
757                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
# Line 586  class MapCanvas(wxWindow): Line 761  class MapCanvas(wxWindow):
761                          select_shape = i                          select_shape = i
762                          break                          break
763              elif shapetype == SHAPETYPE_POINT:              elif shapetype == SHAPETYPE_POINT:
764                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
765                      shape = layer.Shape(i)                      shape = layer.Shape(i)
766                      x, y = shape.Points()[0]                      x, y = shape.Points()[0]
767                      if inverse:                      if inverse:
# Line 603  class MapCanvas(wxWindow): Line 778  class MapCanvas(wxWindow):
778                  return layer, select_shape                  return layer, select_shape
779          return None, None          return None, None
780    
781      def SelectShapeAt(self, x, y):      def SelectShapeAt(self, x, y, layer = None):
782          layer, shape = self.find_shape_at(x, y)          """\
783            Select and return the shape and its layer at window position (x, y)
784    
785            If layer is given, only search in that layer. If no layer is
786            given, search through all layers.
787    
788            Return a tuple (layer, shapeid). If no shape is found, return
789            (None, None).
790            """
791            layer, shape = result = self.find_shape_at(x, y, searched_layer=layer)
792          # If layer is None, then shape will also be None. We don't want          # If layer is None, then shape will also be None. We don't want
793          # to deselect the currently selected layer, so we simply select          # to deselect the currently selected layer, so we simply select
794          # the already selected layer again.          # the already selected layer again.
795          if layer is None:          if layer is None:
796              layer = self.interactor.SelectedLayer()              layer = self.interactor.SelectedLayer()
797          self.interactor.SelectLayerAndShape(layer, shape)          self.interactor.SelectLayerAndShape(layer, shape)
798            return result
799    
800      def LabelShapeAt(self, x, y):      def LabelShapeAt(self, x, y):
801            """Add or remove a label at window position x, y.
802    
803            If there's a label at the given position, remove it. Otherwise
804            determine the shape at the position, run the label dialog and
805            unless the user cancels the dialog, add a laber.
806            """
807          ox = x; oy = y          ox = x; oy = y
808          label_layer = self.map.LabelLayer()          label_layer = self.map.LabelLayer()
809          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)          layer, shape_index = self.find_shape_at(x, y, select_labels = 1)

Legend:
Removed from v.60  
changed lines
  Added in v.295

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26