/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/UI/view.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/UI/view.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26