/[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 149 by bh, Tue May 7 16:41:07 2002 UTC revision 301 by bh, Mon Sep 2 15:59:11 2002 UTC
# 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    
255  class MapCanvas(wxWindow, Publisher):  class MapCanvas(wxWindow, Publisher):
256    
# Line 272  class MapCanvas(wxWindow, Publisher): Line 284  class MapCanvas(wxWindow, Publisher):
284          # To force a redraw call full_redraw().          # To force a redraw call full_redraw().
285          self.redraw_on_idle = 0          self.redraw_on_idle = 0
286    
         # The region to update when idle  
         self.update_region = wx.wxRegion()  
   
287          # the bitmap serving as backing store          # the bitmap serving as backing store
288          self.bitmap = None          self.bitmap = None
289    
# Line 282  class MapCanvas(wxWindow, Publisher): Line 291  class MapCanvas(wxWindow, Publisher):
291          self.interactor = interactor          self.interactor = interactor
292          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)
293    
294            # keep track of which layers/shapes are selected to make sure we
295            # only redraw when necessary
296            self.last_selected_layer = None
297            self.last_selected_shape = None
298    
299          # subscribe the WX events we're interested in          # subscribe the WX events we're interested in
300          EVT_PAINT(self, self.OnPaint)          EVT_PAINT(self, self.OnPaint)
301          EVT_LEFT_DOWN(self, self.OnLeftDown)          EVT_LEFT_DOWN(self, self.OnLeftDown)
# Line 300  class MapCanvas(wxWindow, Publisher): Line 314  class MapCanvas(wxWindow, Publisher):
314          if self.map is not None and self.map.HasLayers():          if self.map is not None and self.map.HasLayers():
315              # We have a non-empty map. Redraw it in idle time              # We have a non-empty map. Redraw it in idle time
316              self.redraw_on_idle = 1              self.redraw_on_idle = 1
             # update the region that has to be redrawn  
             self.update_region.UnionRegion(self.GetUpdateRegion())  
317          else:          else:
318              # 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
319              # the screen.              # the screen.
320                
321              # XXX it's probably possible to get rid of this. The              # XXX it's probably possible to get rid of this. The
322              # background color of the window is already white and the              # background color of the window is already white and the
323              # only thing we may have to do is to call self.Refresh()              # only thing we may have to do is to call self.Refresh()
324              # with a true argument in the right places.              # with a true argument in the right places.
325              dc.BeginDrawing()              dc.BeginDrawing()
326              dc.Clear()                          dc.Clear()
327              dc.EndDrawing()              dc.EndDrawing()
328    
             # clear the region  
             self.update_region = wx.wxRegion()  
   
329      def do_redraw(self):      def do_redraw(self):
330          # This should only be called if we have a non-empty map.          # This should only be called if we have a non-empty map.
331    
         # get the update region and reset it. We're not actually using  
         # it anymore, though.  
         update_box = self.update_region.GetBox()  
         self.update_region = wx.wxRegion()  
   
332          # Get the window size.          # Get the window size.
333          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
334    
# Line 352  class MapCanvas(wxWindow, Publisher): Line 356  class MapCanvas(wxWindow, Publisher):
356              # draw the map into the bitmap              # draw the map into the bitmap
357              renderer = ScreenRenderer(dc, self.scale, self.offset)              renderer = ScreenRenderer(dc, self.scale, self.offset)
358    
359              # Pass the entire bitmap as update_region to the renderer.              # Pass the entire bitmap as update region to the renderer.
360              # We're redrawing the whole bitmap, after all.              # We're redrawing the whole bitmap, after all.
361              renderer.RenderMap(self.map, (0, 0, width, height),              renderer.RenderMap(self.map, (0, 0, width, height),
362                                 selected_layer, selected_shape)                                 selected_layer, selected_shape)
# Line 374  class MapCanvas(wxWindow, Publisher): Line 378  class MapCanvas(wxWindow, Publisher):
378          printout = MapPrintout(self.map)          printout = MapPrintout(self.map)
379          printer.Print(self, printout, wx.true)          printer.Print(self, printout, wx.true)
380          printout.Destroy()          printout.Destroy()
381            
382      def SetMap(self, map):      def SetMap(self, map):
383          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,
384                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
# Line 395  class MapCanvas(wxWindow, Publisher): Line 399  class MapCanvas(wxWindow, Publisher):
399          self.full_redraw()          self.full_redraw()
400    
401      def Map(self):      def Map(self):
402            """Return the map displayed by this canvas"""
403          return self.map          return self.map
404    
405      def redraw(self, *args):      def redraw(self, *args):
# Line 428  class MapCanvas(wxWindow, Publisher): Line 433  class MapCanvas(wxWindow, Publisher):
433          return ((x - offx) / self.scale, (offy - y) / self.scale)          return ((x - offx) / self.scale, (offy - y) / self.scale)
434    
435      def FitRectToWindow(self, rect):      def FitRectToWindow(self, rect):
436            """Fit the rectangular region given by rect into the window.
437            
438            Set scale so that rect (in projected coordinates) just fits into
439            the window and center it.
440            """
441          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
442          llx, lly, urx, ury = rect          llx, lly, urx, ury = rect
443          if llx == urx or lly == ury:          if llx == urx or lly == ury:
# Line 441  class MapCanvas(wxWindow, Publisher): Line 451  class MapCanvas(wxWindow, Publisher):
451          self.set_view_transform(scale, (offx, offy))          self.set_view_transform(scale, (offx, offy))
452    
453      def FitMapToWindow(self):      def FitMapToWindow(self):
454          """\          """Fit the map to the window
455          Set the scale and offset so that the map is centered in the          
456          window          Set the scale so that the map fits exactly into the window and
457            center it in the window.
458          """          """
459          bbox = self.map.ProjectedBoundingBox()          bbox = self.map.ProjectedBoundingBox()
460          if bbox is not None:          if bbox is not None:
# Line 469  class MapCanvas(wxWindow, Publisher): Line 480  class MapCanvas(wxWindow, Publisher):
480          self.set_view_transform(scale, offset)          self.set_view_transform(scale, offset)
481    
482      def ZoomOutToRect(self, rect):      def ZoomOutToRect(self, rect):
483          # rect is given in window coordinates          """Zoom out to fit the currently visible region into rect.
484    
485            The rect parameter is given in window coordinates
486            """
487          # determine the bbox of the displayed region in projected          # determine the bbox of the displayed region in projected
488          # coordinates          # coordinates
489          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
# Line 487  class MapCanvas(wxWindow, Publisher): Line 500  class MapCanvas(wxWindow, Publisher):
500          self.set_view_transform(scale, (offx, offy))          self.set_view_transform(scale, (offx, offy))
501    
502      def Translate(self, dx, dy):      def Translate(self, dx, dy):
503            """Move the map by dx, dy pixels"""
504          offx, offy = self.offset          offx, offy = self.offset
505          self.set_view_transform(self.scale, (offx + dx, offy + dy))          self.set_view_transform(self.scale, (offx + dx, offy + dy))
506    
507      def ZoomInTool(self):      def ZoomInTool(self):
508            """Start the zoom in tool"""
509          self.tool = ZoomInTool(self)          self.tool = ZoomInTool(self)
510    
511      def ZoomOutTool(self):      def ZoomOutTool(self):
512            """Start the zoom out tool"""
513          self.tool = ZoomOutTool(self)          self.tool = ZoomOutTool(self)
514    
515      def PanTool(self):      def PanTool(self):
516            """Start the pan tool"""
517          self.tool = PanTool(self)          self.tool = PanTool(self)
518    
519      def IdentifyTool(self):      def IdentifyTool(self):
520            """Start the identify tool"""
521          self.tool = IdentifyTool(self)          self.tool = IdentifyTool(self)
522    
523      def LabelTool(self):      def LabelTool(self):
524            """Start the label tool"""
525          self.tool = LabelTool(self)          self.tool = LabelTool(self)
526    
527      def CurrentTool(self):      def CurrentTool(self):
528            """Return the name of the current tool or None if no tool is active"""
529          return self.tool and self.tool.Name() or None          return self.tool and self.tool.Name() or None
530    
531      def CurrentPosition(self):      def CurrentPosition(self):
# Line 543  class MapCanvas(wxWindow, Publisher): Line 563  class MapCanvas(wxWindow, Publisher):
563              self.tool.MouseDown(event)              self.tool.MouseDown(event)
564              self.tool.Show(self.drag_dc)              self.tool.Show(self.drag_dc)
565              self.dragging = 1              self.dragging = 1
566            
567      def OnLeftUp(self, event):      def OnLeftUp(self, event):
         self.ReleaseMouse()  
568          self.set_current_position(event)          self.set_current_position(event)
569          if self.dragging:          if self.dragging:
570                self.ReleaseMouse()
571              self.tool.Hide(self.drag_dc)              self.tool.Hide(self.drag_dc)
572              self.tool.MouseUp(event)              self.tool.MouseUp(event)
573              self.drag_dc = None              self.drag_dc = None
# Line 578  class MapCanvas(wxWindow, Publisher): Line 598  class MapCanvas(wxWindow, Publisher):
598          self.full_redraw()          self.full_redraw()
599    
600      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
601          self.full_redraw()          """Redraw the map.
602    
603      def find_shape_at(self, px, py, select_labels = 0, selected_layer = 1):          Receiver for the SELECTED_SHAPE messages. Try to redraw only
604            when necessary.
605            """
606            # A redraw is necessary when the display has to change, which
607            # means that either the status changes from having no selection
608            # to having a selection shape or vice versa, or when the fact
609            # whether there is a selection at all doesn't change, when the
610            # shape which is selected has changed (which means that layer or
611            # shapeid changes).
612            if ((shape is not None or self.last_selected_shape is not None)
613                and (shape != self.last_selected_shape
614                     or layer != self.last_selected_layer)):
615                self.full_redraw()
616    
617            # remember the selection so we can compare when it changes again.
618            self.last_selected_layer = layer
619            self.last_selected_shape = shape
620    
621        def unprojected_rect_around_point(self, x, y, dist):
622            """return a rect dist pixels around (x, y) in unprojected corrdinates
623    
624            The return value is a tuple (minx, miny, maxx, maxy) suitable a
625            parameter to a layer's ShapesInRegion method.
626            """
627            map_proj = self.map.projection
628            if map_proj is not None:
629                inverse = map_proj.Inverse
630            else:
631                inverse = None
632    
633            xs = []
634            ys = []
635            for dx, dy in ((-1, -1), (1, -1), (1, 1), (-1, 1)):
636                px, py = self.win_to_proj(x + dist * dx, y + dist * dy)
637                if inverse:
638                    px, py = inverse(px, py)
639                xs.append(px)
640                ys.append(py)
641            return (min(xs), min(ys), max(xs), max(ys))
642    
643        def find_shape_at(self, px, py, select_labels = 0, searched_layer = None):
644          """Determine the shape at point px, py in window coords          """Determine the shape at point px, py in window coords
645    
646          Return the shape and the corresponding layer as a tuple (layer,          Return the shape and the corresponding layer as a tuple (layer,
# Line 590  class MapCanvas(wxWindow, Publisher): Line 650  class MapCanvas(wxWindow, Publisher):
650          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
651          as the shape and None as the layer.          as the shape and None as the layer.
652    
653          If the optional parameter selected_layer is true (default), only          If the optional parameter searched_layer is given (or not None
654          search in the currently selected layer.          which it defaults to), only search in that layer.
655          """          """
656          map_proj = self.map.projection          map_proj = self.map.projection
657          if map_proj is not None:          if map_proj is not None:
# Line 604  class MapCanvas(wxWindow, Publisher): Line 664  class MapCanvas(wxWindow, Publisher):
664    
665          if select_labels:          if select_labels:
666              labels = self.map.LabelLayer().Labels()              labels = self.map.LabelLayer().Labels()
667                
668              if labels:              if labels:
669                  dc = wxClientDC(self)                  dc = wxClientDC(self)
670                  font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)                  font = wxFont(10, wx.wxSWISS, wx.wxNORMAL, wx.wxNORMAL)
# Line 636  class MapCanvas(wxWindow, Publisher): Line 696  class MapCanvas(wxWindow, Publisher):
696                      if x <= px < x + width and y <= py <= y + height:                      if x <= px < x + width and y <= py <= y + height:
697                          return None, i                          return None, i
698    
699          if selected_layer:          if searched_layer:
700              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 = []  
701          else:          else:
702              layers = self.map.Layers()              layers = self.map.Layers()
703    
# Line 656  class MapCanvas(wxWindow, Publisher): Line 710  class MapCanvas(wxWindow, Publisher):
710    
711              filled = layer.fill is not None              filled = layer.fill is not None
712              stroked = layer.stroke is not None              stroked = layer.stroke is not None
713                    
714              layer_proj = layer.projection              layer_proj = layer.projection
715              if layer_proj is not None:              if layer_proj is not None:
716                  inverse = layer_proj.Inverse                  inverse = layer_proj.Inverse
717              else:              else:
718                  inverse = None                  inverse = None
719                    
720              shapetype = layer.ShapeType()              shapetype = layer.ShapeType()
721    
722              select_shape = -1              select_shape = -1
723    
724                # Determine the ids of the shapes that overlap a tiny area
725                # around the point. For layers containing points we have to
726                # choose a larger size of the box we're testing agains so
727                # that we take the size of the markers into account
728                # FIXME: Once the markers are more flexible this part has to
729                # become more flexible too, of course
730                if shapetype == SHAPETYPE_POINT:
731                    box = self.unprojected_rect_around_point(px, py, 5)
732                else:
733                    box = self.unprojected_rect_around_point(px, py, 1)
734                shape_ids = layer.ShapesInRegion(box)
735                shape_ids.reverse()
736    
737              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
738                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
739                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
740                                                      i,                                                      i,
741                                                      filled, stroked,                                                      filled, stroked,
# Line 678  class MapCanvas(wxWindow, Publisher): Line 746  class MapCanvas(wxWindow, Publisher):
746                          select_shape = i                          select_shape = i
747                          break                          break
748              elif shapetype == SHAPETYPE_ARC:              elif shapetype == SHAPETYPE_ARC:
749                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
750                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      result = point_in_polygon_shape(layer.shapefile.cobject(),
751                                                      i, 0, 1,                                                      i, 0, 1,
752                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
# Line 688  class MapCanvas(wxWindow, Publisher): Line 756  class MapCanvas(wxWindow, Publisher):
756                          select_shape = i                          select_shape = i
757                          break                          break
758              elif shapetype == SHAPETYPE_POINT:              elif shapetype == SHAPETYPE_POINT:
759                  for i in range(layer.NumShapes() - 1, -1, -1):                  for i in shape_ids:
760                      shape = layer.Shape(i)                      shape = layer.Shape(i)
761                      x, y = shape.Points()[0]                      x, y = shape.Points()[0]
762                      if inverse:                      if inverse:
# Line 705  class MapCanvas(wxWindow, Publisher): Line 773  class MapCanvas(wxWindow, Publisher):
773                  return layer, select_shape                  return layer, select_shape
774          return None, None          return None, None
775    
776      def SelectShapeAt(self, x, y):      def SelectShapeAt(self, x, y, layer = None):
777          layer, shape = self.find_shape_at(x, y, selected_layer = 0)          """\
778            Select and return the shape and its layer at window position (x, y)
779    
780            If layer is given, only search in that layer. If no layer is
781            given, search through all layers.
782    
783            Return a tuple (layer, shapeid). If no shape is found, return
784            (None, None).
785            """
786            layer, shape = result = self.find_shape_at(x, y, searched_layer=layer)
787          # 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
788          # to deselect the currently selected layer, so we simply select          # to deselect the currently selected layer, so we simply select
789          # the already selected layer again.          # the already selected layer again.
790          if layer is None:          if layer is None:
791              layer = self.interactor.SelectedLayer()              layer = self.interactor.SelectedLayer()
792          self.interactor.SelectLayerAndShape(layer, shape)          self.interactor.SelectLayerAndShape(layer, shape)
793            return result
794    
795      def LabelShapeAt(self, x, y):      def LabelShapeAt(self, x, y):
796            """Add or remove a label at window position x, y.
797    
798            If there's a label at the given position, remove it. Otherwise
799            determine the shape at the position, run the label dialog and
800            unless the user cancels the dialog, add a laber.
801            """
802          ox = x; oy = y          ox = x; oy = y
803          label_layer = self.map.LabelLayer()          label_layer = self.map.LabelLayer()
804          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.149  
changed lines
  Added in v.301

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26