/[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 301 by bh, Mon Sep 2 15:59:11 2002 UTC revision 1285 by jonathan, Mon Jun 23 10:30:53 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4    # Frank Koormann <[email protected]>
5  #  #
6  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
7  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 11  Classes for display of a map and interac Line 12  Classes for display of a map and interac
12    
13  __version__ = "$Revision$"  __version__ = "$Revision$"
14    
15    from Thuban import _
16    
17    import sys
18    import os.path
19    
20  from math import hypot  from math import hypot
21    
22  from wxPython.wx import wxWindow,\  from wxPython.wx import wxWindow, \
23       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\       wxPaintDC, wxColour, wxClientDC, wxINVERT, wxTRANSPARENT_BRUSH, wxFont,\
24       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW       EVT_PAINT, EVT_LEFT_DOWN, EVT_LEFT_UP, EVT_MOTION, EVT_LEAVE_WINDOW, \
25         wxBITMAP_TYPE_XPM, wxCursor, wxImageFromBitmap, wxPlatform, \
26         wxBeginBusyCursor, wxEndBusyCursor
27    
28    
29    # Export related stuff
30    if wxPlatform == '__WXMSW__':
31        from wxPython.wx import wxMetaFileDC
32    from wxPython.wx import wxFileDialog, wxSAVE, wxOVERWRITE_PROMPT, wxID_OK
33    
34  from wxPython import wx  from wxPython import wx
35    
36  from wxproj import point_in_polygon_shape, shape_centroid  from wxproj import point_in_polygon_shape, shape_centroid
37    
   
38  from Thuban.Model.messages import MAP_PROJECTION_CHANGED, \  from Thuban.Model.messages import MAP_PROJECTION_CHANGED, \
39       LAYERS_CHANGED, LAYER_LEGEND_CHANGED, LAYER_VISIBILITY_CHANGED       LAYER_PROJECTION_CHANGED, \
40         MAP_LAYERS_CHANGED, LAYER_CHANGED, LAYER_VISIBILITY_CHANGED
41  from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \  from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \
42       SHAPETYPE_POINT       SHAPETYPE_POINT
43  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
44       ALIGN_LEFT, ALIGN_RIGHT       ALIGN_LEFT, ALIGN_RIGHT
45  from Thuban.Lib.connector import Publisher  from Thuban.Lib.connector import Publisher
46    from Thuban.Model.color import Color
47    
48  from renderer import ScreenRenderer, PrinterRender  import resource
49    
50    from selection import Selection
51    from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
52    
53  import labeldialog  import labeldialog
54    
55  from messages import SELECTED_SHAPE, VIEW_POSITION  from messages import LAYER_SELECTED, SHAPES_SELECTED, VIEW_POSITION, \
56                         SCALE_CHANGED
57    
58    
59  #  #
# Line 215  class LabelTool(Tool): Line 233  class LabelTool(Tool):
233          self.view.LabelShapeAt(event.m_x, event.m_y)          self.view.LabelShapeAt(event.m_x, event.m_y)
234    
235    
   
   
236  class MapPrintout(wx.wxPrintout):  class MapPrintout(wx.wxPrintout):
237    
238      """      """
239      wxPrintout class for printing Thuban maps      wxPrintout class for printing Thuban maps
240      """      """
241    
242      def __init__(self, map):      def __init__(self, canvas, map, region, selected_layer, selected_shapes):
243          wx.wxPrintout.__init__(self)          wx.wxPrintout.__init__(self)
244            self.canvas = canvas
245          self.map = map          self.map = map
246            self.region = region
247            self.selected_layer = selected_layer
248            self.selected_shapes = selected_shapes
249    
250      def GetPageInfo(self):      def GetPageInfo(self):
251          return (1, 1, 1, 1)          return (1, 1, 1, 1)
# Line 239  class MapPrintout(wx.wxPrintout): Line 259  class MapPrintout(wx.wxPrintout):
259    
260      def draw_on_dc(self, dc):      def draw_on_dc(self, dc):
261          width, height = self.GetPageSizePixels()          width, height = self.GetPageSizePixels()
262          llx, lly, urx, ury = self.map.ProjectedBoundingBox()          scale, offset, mapregion = OutputTransform(self.canvas.scale,
263          scalex = width / (urx - llx)                                                     self.canvas.offset,
264          scaley = height / (ury - lly)                                                     self.canvas.GetSizeTuple(),
265          scale = min(scalex, scaley)                                                     self.GetPageSizePixels())
         offx = 0.5 * (width - (urx + llx) * scale)  
         offy = 0.5 * (height + (ury + lly) * scale)  
   
266          resx, resy = self.GetPPIPrinter()          resx, resy = self.GetPPIPrinter()
267          renderer = PrinterRender(dc, scale, (offx, offy), resolution = resx)          renderer = PrinterRenderer(dc, scale, offset, resolution = resy)
268          renderer.RenderMap(self.map)          x, y, width, height = self.region
269          return wx.true          canvas_scale = self.canvas.scale
270            renderer.RenderMap(self.map,
271                               (0,0,
272                                    (width/canvas_scale)*scale,
273                                    (height/canvas_scale)*scale),
274                                    mapregion,
275                               self.selected_layer, self.selected_shapes)
276            return True
277    
278  class MapCanvas(wxWindow, Publisher):  class MapCanvas(wxWindow, Publisher):
279    
280      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
281    
282      def __init__(self, parent, winid, interactor):      # Some messages that can be subscribed/unsubscribed directly through
283        # the MapCanvas come in fact from other objects. This is a dict
284        # mapping those messages to the names of the instance variables they
285        # actually come from. The delegation is implemented in the Subscribe
286        # and Unsubscribe methods
287        delegated_messages = {LAYER_SELECTED: "selection",
288                              SHAPES_SELECTED: "selection"}
289    
290        # Methods delegated to some instance variables. The delegation is
291        # implemented in the __getattr__ method.
292        delegated_methods = {"SelectLayer": "selection",
293                             "SelectShapes": "selection",
294                             "SelectedLayer": "selection",
295                             "HasSelectedLayer": "selection",
296                             "HasSelectedShapes": "selection",
297                             "SelectedShapes": "selection"}
298    
299        def __init__(self, parent, winid):
300          wxWindow.__init__(self, parent, winid)          wxWindow.__init__(self, parent, winid)
301          self.SetBackgroundColour(wxColour(255, 255, 255))          self.SetBackgroundColour(wxColour(255, 255, 255))
302    
303          # the map displayed in this canvas. Set with SetMap()          # the map displayed in this canvas. Set with SetMap()
304          self.map = None          self.map = None
305    
306            # current map projection. should only differ from map.projection
307            # when the map's projection is changing and we need access to the
308            # old projection.
309            self.current_map_proj = None
310    
311          # scale and offset describe the transformation from projected          # scale and offset describe the transformation from projected
312          # coordinates to window coordinates.          # coordinates to window coordinates.
313          self.scale = 1.0          self.scale = 1.0
# Line 279  class MapCanvas(wxWindow, Publisher): Line 324  class MapCanvas(wxWindow, Publisher):
324          # if the mouse is outside the window.          # if the mouse is outside the window.
325          self.current_position = None          self.current_position = None
326    
         # If true, OnIdle will call do_redraw to do the actual  
         # redrawing. Set by OnPaint to avoid some unnecessary redraws.  
         # To force a redraw call full_redraw().  
         self.redraw_on_idle = 0  
   
327          # the bitmap serving as backing store          # the bitmap serving as backing store
328          self.bitmap = None          self.bitmap = None
329    
330          # the interactor          # the selection
331          self.interactor = interactor          self.selection = Selection()
332          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)          self.selection.Subscribe(SHAPES_SELECTED , self.shape_selected)
333    
334          # keep track of which layers/shapes are selected to make sure we          # keep track of which layers/shapes are selected to make sure we
335          # only redraw when necessary          # only redraw when necessary
# Line 303  class MapCanvas(wxWindow, Publisher): Line 343  class MapCanvas(wxWindow, Publisher):
343          EVT_MOTION(self, self.OnMotion)          EVT_MOTION(self, self.OnMotion)
344          EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)          EVT_LEAVE_WINDOW(self, self.OnLeaveWindow)
345          wx.EVT_SIZE(self, self.OnSize)          wx.EVT_SIZE(self, self.OnSize)
         wx.EVT_IDLE(self, self.OnIdle)  
346    
347      def __del__(self):      def __del__(self):
348          wxWindow.__del__(self)          wxWindow.__del__(self)
349          Publisher.__del__(self)          Publisher.__del__(self)
350    
351        def Subscribe(self, channel, *args):
352            """Extend the inherited method to handle delegated messages.
353    
354            If channel is one of the delegated messages call the appropriate
355            object's Subscribe method. Otherwise just call the inherited
356            method.
357            """
358            if channel in self.delegated_messages:
359                object = getattr(self, self.delegated_messages[channel])
360                object.Subscribe(channel, *args)
361            else:
362                Publisher.Subscribe(self, channel, *args)
363    
364        def Unsubscribe(self, channel, *args):
365            """Extend the inherited method to handle delegated messages.
366    
367            If channel is one of the delegated messages call the appropriate
368            object's Unsubscribe method. Otherwise just call the inherited
369            method.
370            """
371            if channel in self.delegated_messages:
372                object = getattr(self, self.delegated_messages[channel])
373                object.Unsubscribe(channel, *args)
374            else:
375                Publisher.Unsubscribe(self, channel, *args)
376    
377        def __getattr__(self, attr):
378            if attr in self.delegated_methods:
379                return getattr(getattr(self, self.delegated_methods[attr]), attr)
380            raise AttributeError(attr)
381    
382      def OnPaint(self, event):      def OnPaint(self, event):
383          dc = wxPaintDC(self)          dc = wxPaintDC(self)
384          if self.map is not None and self.map.HasLayers():  
385              # We have a non-empty map. Redraw it in idle time          clear = self.map is None or not self.map.HasLayers()
386              self.redraw_on_idle = 1  
387          else:          wxBeginBusyCursor()
388              # If we've got no map or if the map is empty, simply clear          try:
389              # the screen.              if not clear:
390                    self.do_redraw()
391              # XXX it's probably possible to get rid of this. The              else:
392              # background color of the window is already white and the                  # If we've got no map or if the map is empty, simply clear
393              # only thing we may have to do is to call self.Refresh()                  # the screen.
394              # with a true argument in the right places.  
395              dc.BeginDrawing()                  # XXX it's probably possible to get rid of this. The
396              dc.Clear()                  # background color of the window is already white and the
397              dc.EndDrawing()                  # only thing we may have to do is to call self.Refresh()
398                    # with a true argument in the right places.
399                    dc.BeginDrawing()
400                    dc.Clear()
401                    dc.EndDrawing()
402            finally:
403                wxEndBusyCursor()
404    
405      def do_redraw(self):      def do_redraw(self):
406          # This should only be called if we have a non-empty map.          # This should only be called if we have a non-empty map.
# Line 342  class MapCanvas(wxWindow, Publisher): Line 418  class MapCanvas(wxWindow, Publisher):
418              dc.BeginDrawing()              dc.BeginDrawing()
419    
420              # clear the background              # clear the background
421              dc.SetBrush(wx.wxWHITE_BRUSH)              #dc.SetBrush(wx.wxWHITE_BRUSH)
422              dc.SetPen(wx.wxTRANSPARENT_PEN)              #dc.SetPen(wx.wxTRANSPARENT_PEN)
423              dc.DrawRectangle(0, 0, width, height)              #dc.DrawRectangle(0, 0, width, height)
424                dc.SetBackground(wx.wxWHITE_BRUSH)
425              if 1: #self.interactor.selected_map is self.map:              dc.Clear()
426                  selected_layer = self.interactor.selected_layer  
427                  selected_shape = self.interactor.selected_shape              selected_layer = self.selection.SelectedLayer()
428              else:              selected_shapes = self.selection.SelectedShapes()
                 selected_layer = None  
                 selected_shape = None  
429    
430              # draw the map into the bitmap              # draw the map into the bitmap
431              renderer = ScreenRenderer(dc, self.scale, self.offset)              renderer = ScreenRenderer(dc, self.scale, self.offset)
# Line 359  class MapCanvas(wxWindow, Publisher): Line 433  class MapCanvas(wxWindow, Publisher):
433              # Pass the entire bitmap as update region to the renderer.              # Pass the entire bitmap as update region to the renderer.
434              # We're redrawing the whole bitmap, after all.              # We're redrawing the whole bitmap, after all.
435              renderer.RenderMap(self.map, (0, 0, width, height),              renderer.RenderMap(self.map, (0, 0, width, height),
436                                 selected_layer, selected_shape)                                 selected_layer, selected_shapes)
437    
438              dc.EndDrawing()              dc.EndDrawing()
439              dc.SelectObject(wx.wxNullBitmap)              dc.SelectObject(wx.wxNullBitmap)
# Line 373  class MapCanvas(wxWindow, Publisher): Line 447  class MapCanvas(wxWindow, Publisher):
447          clientdc.Blit(0, 0, width, height, dc, 0, 0)          clientdc.Blit(0, 0, width, height, dc, 0, 0)
448          clientdc.EndDrawing()          clientdc.EndDrawing()
449    
450        def Export(self):
451            if self.scale == 0:
452                return
453    
454            if hasattr(self, "export_path"):
455                export_path = self.export_path
456            else:
457                export_path="."
458            dlg = wxFileDialog(self, _("Export Map"), export_path, "",
459                               "Enhanced Metafile (*.wmf)|*.wmf",
460                               wxSAVE|wxOVERWRITE_PROMPT)
461            if dlg.ShowModal() == wxID_OK:
462                self.export_path = os.path.dirname(dlg.GetPath())
463                dc = wxMetaFileDC(dlg.GetPath())
464        
465                scale, offset, mapregion = OutputTransform(self.scale,
466                                                           self.offset,
467                                                           self.GetSizeTuple(),
468                                                           dc.GetSizeTuple())
469    
470                selected_layer = self.selection.SelectedLayer()
471                selected_shapes = self.selection.SelectedShapes()
472    
473                renderer = ExportRenderer(dc, scale, offset)
474    
475                # Pass the entire bitmap as update region to the renderer.
476                # We're redrawing the whole bitmap, after all.
477                width, height = self.GetSizeTuple()
478                renderer.RenderMap(self.map,
479                                    (0,0,
480                                        (width/self.scale)*scale,
481                                        (height/self.scale)*scale),
482                                    mapregion,
483                                    selected_layer, selected_shapes)
484                dc.EndDrawing()
485                dc.Close()
486            dlg.Destroy()
487            
488      def Print(self):      def Print(self):
489          printer = wx.wxPrinter()          printer = wx.wxPrinter()
490          printout = MapPrintout(self.map)          width, height = self.GetSizeTuple()
491          printer.Print(self, printout, wx.true)          selected_layer = self.selection.SelectedLayer()
492            selected_shapes = self.selection.SelectedShapes()
493            
494            printout = MapPrintout(self, self.map, (0, 0, width, height),
495                                   selected_layer, selected_shapes)
496            printer.Print(self, printout, True)
497          printout.Destroy()          printout.Destroy()
498    
499      def SetMap(self, map):      def SetMap(self, map):
500          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,          redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
501                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
502          if self.map is not None:          if self.map is not None:
503              for channel in redraw_channels:              for channel in redraw_channels:
504                  self.map.Unsubscribe(channel, self.full_redraw)                  self.map.Unsubscribe(channel, self.full_redraw)
505              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,
506                                   self.projection_changed)                                   self.map_projection_changed)
507                self.map.Unsubscribe(LAYER_PROJECTION_CHANGED,
508                                     self.layer_projection_changed)
509          self.map = map          self.map = map
510            self.current_map_proj = self.map.GetProjection()
511            self.selection.ClearSelection()
512          if self.map is not None:          if self.map is not None:
513              for channel in redraw_channels:              for channel in redraw_channels:
514                  self.map.Subscribe(channel, self.full_redraw)                  self.map.Subscribe(channel, self.full_redraw)
515              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.map_projection_changed)
516                self.map.Subscribe(LAYER_PROJECTION_CHANGED, self.layer_projection_changed)
517          self.FitMapToWindow()          self.FitMapToWindow()
518          # 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
519          # by FitMapToWindow but if map is empty it hasn't been called          # by FitMapToWindow but if map is empty it hasn't been called
# Line 409  class MapCanvas(wxWindow, Publisher): Line 531  class MapCanvas(wxWindow, Publisher):
531          self.bitmap = None          self.bitmap = None
532          self.redraw()          self.redraw()
533    
534      def projection_changed(self, *args):      def map_projection_changed(self, *args):
535          self.FitMapToWindow()  
536            proj = self.current_map_proj
537            self.current_map_proj = self.map.GetProjection()
538    
539            bbox = None
540    
541            if proj is not None and self.current_map_proj is not None:
542                width, height = self.GetSizeTuple()
543                llx, lly = self.win_to_proj(0, height)
544                urx, ury = self.win_to_proj(width, 0)
545                bbox = proj.Inverse(llx, lly) + proj.Inverse(urx, ury)
546                bbox = self.current_map_proj.ForwardBBox(bbox)
547    
548            if bbox is not None:
549                self.FitRectToWindow(bbox)
550            else:
551                self.FitMapToWindow()
552    
553            self.full_redraw()
554    
555        def layer_projection_changed(self, *args):
556          self.full_redraw()          self.full_redraw()
557    
558      def set_view_transform(self, scale, offset):      def set_view_transform(self, scale, offset):
559            # width/height of the projected bbox
560            llx, lly, urx, ury = bbox = self.map.ProjectedBoundingBox()
561            pwidth = float(urx - llx)
562            pheight = float(ury - lly)
563    
564            # width/height of the window
565            wwidth, wheight = self.GetSizeTuple()
566    
567            # The window's center in projected coordinates assuming the new
568            # scale/offset
569            pcenterx = (wwidth/2 - offset[0]) / scale
570            pcentery = (offset[1] - wheight/2) / scale
571    
572            # The window coordinates used when drawing the shapes must fit
573            # into 16bit signed integers.
574            max_len = max(pwidth, pheight)
575            if max_len:
576                max_scale = 32000.0 / max_len
577            else:
578                # FIXME: What to do in this case? The bbox is effectively
579                # empty so any scale should work.
580                max_scale = scale
581    
582            # The minimal scale is somewhat arbitrarily set to half that of
583            # the bbox fit into the window
584            scales = []
585            if pwidth:
586                scales.append(wwidth / pwidth)
587            if pheight:
588                scales.append(wheight / pheight)
589            if scales:
590                min_scale = 0.5 * min(scales)
591            else:
592                min_scale = scale
593    
594            if scale > max_scale:
595                scale = max_scale
596            elif scale < min_scale:
597                scale = min_scale
598    
599          self.scale = scale          self.scale = scale
600          self.offset = offset  
601            # determine new offset to preserve the center
602            self.offset = (wwidth/2 - scale * pcenterx,
603                           wheight/2 + scale * pcentery)
604          self.full_redraw()          self.full_redraw()
605            self.issue(SCALE_CHANGED, scale)
606    
607      def proj_to_win(self, x, y):      def proj_to_win(self, x, y):
608          """\          """\
609          Return the point in  window coords given by projected coordinates x y          Return the point in  window coords given by projected coordinates x y
610          """          """
611            if self.scale == 0:
612                return (0, 0)
613    
614          offx, offy = self.offset          offx, offy = self.offset
615          return (self.scale * x + offx, -self.scale * y + offy)          return (self.scale * x + offx, -self.scale * y + offy)
616    
# Line 429  class MapCanvas(wxWindow, Publisher): Line 618  class MapCanvas(wxWindow, Publisher):
618          """\          """\
619          Return the point in projected coordinates given by window coords x y          Return the point in projected coordinates given by window coords x y
620          """          """
621            if self.scale == 0:
622                return (0, 0)
623    
624          offx, offy = self.offset          offx, offy = self.offset
625          return ((x - offx) / self.scale, (offy - y) / self.scale)          return ((x - offx) / self.scale, (offy - y) / self.scale)
626    
627      def FitRectToWindow(self, rect):      def FitRectToWindow(self, rect):
628          """Fit the rectangular region given by rect into the window.          """Fit the rectangular region given by rect into the window.
629            
630          Set scale so that rect (in projected coordinates) just fits into          Set scale so that rect (in projected coordinates) just fits into
631          the window and center it.          the window and center it.
632          """          """
633          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
634          llx, lly, urx, ury = rect          llx, lly, urx, ury = rect
635          if llx == urx or lly == ury:          if llx == urx or lly == ury:
636              # zero with or zero height. Do Nothing              # zero width or zero height. Do Nothing
637              return              return
638          scalex = width / (urx - llx)          scalex = width / (urx - llx)
639          scaley = height / (ury - lly)          scaley = height / (ury - lly)
# Line 452  class MapCanvas(wxWindow, Publisher): Line 644  class MapCanvas(wxWindow, Publisher):
644    
645      def FitMapToWindow(self):      def FitMapToWindow(self):
646          """Fit the map to the window          """Fit the map to the window
647            
648          Set the scale so that the map fits exactly into the window and          Set the scale so that the map fits exactly into the window and
649          center it in the window.          center it in the window.
650          """          """
651          bbox = self.map.ProjectedBoundingBox()          if self.map is not None:
652                bbox = self.map.ProjectedBoundingBox()
653                if bbox is not None:
654                    self.FitRectToWindow(bbox)
655    
656        def FitLayerToWindow(self, layer):
657            """Fit the given layer to the window.
658    
659            Set the scale so that the layer fits exactly into the window and
660            center it in the window.
661            """
662            
663            bbox = layer.LatLongBoundingBox()
664          if bbox is not None:          if bbox is not None:
665              self.FitRectToWindow(bbox)              proj = self.map.GetProjection()
666                if proj is not None:
667                    bbox = proj.ForwardBBox(bbox)
668    
669                if bbox is not None:
670                    self.FitRectToWindow(bbox)
671    
672        def FitSelectedToWindow(self):
673            layer = self.selection.SelectedLayer()
674            shapes = self.selection.SelectedShapes()
675    
676            bbox = layer.ShapesBoundingBox(shapes)
677            if bbox is not None:
678                proj = self.map.GetProjection()
679                if proj is not None:
680                    bbox = proj.ForwardBBox(bbox)
681    
682                if bbox is not None:
683                    if len(shapes) == 1 and layer.ShapeType() == SHAPETYPE_POINT:
684                        self.ZoomFactor(1, self.proj_to_win(bbox[0], bbox[1]))
685                    else:
686                        self.FitRectToWindow(bbox)
687    
688      def ZoomFactor(self, factor, center = None):      def ZoomFactor(self, factor, center = None):
689          """Multiply the zoom by factor and center on center.          """Multiply the zoom by factor and center on center.
# Line 467  class MapCanvas(wxWindow, Publisher): Line 692  class MapCanvas(wxWindow, Publisher):
692          that should be centered. If it is omitted, it defaults to the          that should be centered. If it is omitted, it defaults to the
693          center of the window          center of the window
694          """          """
695          width, height = self.GetSizeTuple()          if self.scale > 0:
696          scale = self.scale * factor              width, height = self.GetSizeTuple()
697          offx, offy = self.offset              scale = self.scale * factor
698          if center is not None:              offx, offy = self.offset
699              cx, cy = center              if center is not None:
700          else:                  cx, cy = center
701              cx = width / 2              else:
702              cy = height / 2                  cx = width / 2
703          offset = (factor * (offx - cx) + width / 2,                  cy = height / 2
704                    factor * (offy - cy) + height / 2)              offset = (factor * (offx - cx) + width / 2,
705          self.set_view_transform(scale, offset)                      factor * (offy - cy) + height / 2)
706                self.set_view_transform(scale, offset)
707    
708      def ZoomOutToRect(self, rect):      def ZoomOutToRect(self, rect):
709          """Zoom out to fit the currently visible region into rect.          """Zoom out to fit the currently visible region into rect.
# Line 504  class MapCanvas(wxWindow, Publisher): Line 730  class MapCanvas(wxWindow, Publisher):
730          offx, offy = self.offset          offx, offy = self.offset
731          self.set_view_transform(self.scale, (offx + dx, offy + dy))          self.set_view_transform(self.scale, (offx + dx, offy + dy))
732    
733        def SelectTool(self, tool):
734            """Make tool the active tool.
735    
736            The parameter should be an instance of Tool or None to indicate
737            that no tool is active.
738            """
739            self.tool = tool
740    
741      def ZoomInTool(self):      def ZoomInTool(self):
742          """Start the zoom in tool"""          """Start the zoom in tool"""
743          self.tool = ZoomInTool(self)          self.SelectTool(ZoomInTool(self))
744    
745      def ZoomOutTool(self):      def ZoomOutTool(self):
746          """Start the zoom out tool"""          """Start the zoom out tool"""
747          self.tool = ZoomOutTool(self)          self.SelectTool(ZoomOutTool(self))
748    
749      def PanTool(self):      def PanTool(self):
750          """Start the pan tool"""          """Start the pan tool"""
751          self.tool = PanTool(self)          self.SelectTool(PanTool(self))
752            #img = resource.GetImageResource("pan", wxBITMAP_TYPE_XPM)
753            #bmp = resource.GetBitmapResource("pan", wxBITMAP_TYPE_XPM)
754            #print bmp
755            #img = wxImageFromBitmap(bmp)
756            #print img
757            #cur = wxCursor(img)
758            #print cur
759            #self.SetCursor(cur)
760    
761      def IdentifyTool(self):      def IdentifyTool(self):
762          """Start the identify tool"""          """Start the identify tool"""
763          self.tool = IdentifyTool(self)          self.SelectTool(IdentifyTool(self))
764    
765      def LabelTool(self):      def LabelTool(self):
766          """Start the label tool"""          """Start the label tool"""
767          self.tool = LabelTool(self)          self.SelectTool(LabelTool(self))
768    
769      def CurrentTool(self):      def CurrentTool(self):
770          """Return the name of the current tool or None if no tool is active"""          """Return the name of the current tool or None if no tool is active"""
# Line 568  class MapCanvas(wxWindow, Publisher): Line 810  class MapCanvas(wxWindow, Publisher):
810          self.set_current_position(event)          self.set_current_position(event)
811          if self.dragging:          if self.dragging:
812              self.ReleaseMouse()              self.ReleaseMouse()
813              self.tool.Hide(self.drag_dc)              try:
814              self.tool.MouseUp(event)                  self.tool.Hide(self.drag_dc)
815              self.drag_dc = None                  self.tool.MouseUp(event)
816          self.dragging = 0              finally:
817                    self.drag_dc = None
818                    self.dragging = 0
819    
820      def OnMotion(self, event):      def OnMotion(self, event):
821          self.set_current_position(event)          self.set_current_position(event)
# Line 583  class MapCanvas(wxWindow, Publisher): Line 827  class MapCanvas(wxWindow, Publisher):
827      def OnLeaveWindow(self, event):      def OnLeaveWindow(self, event):
828          self.set_current_position(None)          self.set_current_position(None)
829    
     def OnIdle(self, event):  
         if self.redraw_on_idle:  
             self.do_redraw()  
         self.redraw_on_idle = 0  
   
830      def OnSize(self, event):      def OnSize(self, event):
831          # the window's size has changed. We have to get a new bitmap. If          # the window's size has changed. We have to get a new bitmap. If
832          # we want to be clever we could try to get by without throwing          # we want to be clever we could try to get by without throwing
# Line 596  class MapCanvas(wxWindow, Publisher): Line 835  class MapCanvas(wxWindow, Publisher):
835          # Even when the window becomes larger some parts of the bitmap          # Even when the window becomes larger some parts of the bitmap
836          # could be reused.          # could be reused.
837          self.full_redraw()          self.full_redraw()
838            pass
839    
840      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
841          """Redraw the map.          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
842            # The selection object takes care that it only issues
843          Receiver for the SELECTED_SHAPE messages. Try to redraw only          # SHAPES_SELECTED messages when the set of selected shapes has
844          when necessary.          # actually changed, so we can do a full redraw unconditionally.
845          """          # FIXME: We should perhaps try to limit the redraw to the are
846          # A redraw is necessary when the display has to change, which          # actually covered by the shapes before and after the selection
847          # means that either the status changes from having no selection          # change.
848          # to having a selection shape or vice versa, or when the fact          self.full_redraw()
         # whether there is a selection at all doesn't change, when the  
         # shape which is selected has changed (which means that layer or  
         # shapeid changes).  
         if ((shape is not None or self.last_selected_shape is not None)  
             and (shape != self.last_selected_shape  
                  or layer != self.last_selected_layer)):  
             self.full_redraw()  
   
         # remember the selection so we can compare when it changes again.  
         self.last_selected_layer = layer  
         self.last_selected_shape = shape  
849    
850      def unprojected_rect_around_point(self, x, y, dist):      def unprojected_rect_around_point(self, x, y, dist):
851          """return a rect dist pixels around (x, y) in unprojected corrdinates          """return a rect dist pixels around (x, y) in unprojected corrdinates
# Line 660  class MapCanvas(wxWindow, Publisher): Line 889  class MapCanvas(wxWindow, Publisher):
889              forward = None              forward = None
890    
891          scale = self.scale          scale = self.scale
892    
893            if scale == 0:
894                return None, None
895    
896          offx, offy = self.offset          offx, offy = self.offset
897    
898          if select_labels:          if select_labels:
# Line 705  class MapCanvas(wxWindow, Publisher): Line 938  class MapCanvas(wxWindow, Publisher):
938              layer = layers[layer_index]              layer = layers[layer_index]
939    
940              # search only in visible layers              # search only in visible layers
941              if not layer.Visible():              if not layer.Visible() or not layer.HasShapes():
942                  continue                  continue
943    
944              filled = layer.fill is not None              filled = layer.GetClassification().GetDefaultFill() \
945              stroked = layer.stroke is not None                       is not Color.Transparent
946                stroked = layer.GetClassification().GetDefaultLineColor() \
947                          is not Color.Transparent
948    
949              layer_proj = layer.projection              layer_proj = layer.projection
950              if layer_proj is not None:              if layer_proj is not None:
# Line 736  class MapCanvas(wxWindow, Publisher): Line 971  class MapCanvas(wxWindow, Publisher):
971    
972              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
973                  for i in shape_ids:                  for i in shape_ids:
974                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      shapefile = layer.ShapeStore().Shapefile().cobject()
975                                                      i,                      result = point_in_polygon_shape(shapefile, i,
976                                                      filled, stroked,                                                      filled, stroked,
977                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
978                                                      scale, -scale, offx, offy,                                                      scale, -scale, offx, offy,
# Line 747  class MapCanvas(wxWindow, Publisher): Line 982  class MapCanvas(wxWindow, Publisher):
982                          break                          break
983              elif shapetype == SHAPETYPE_ARC:              elif shapetype == SHAPETYPE_ARC:
984                  for i in shape_ids:                  for i in shape_ids:
985                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      shapefile = layer.ShapeStore().Shapefile().cobject()
986                        result = point_in_polygon_shape(shapefile,
987                                                      i, 0, 1,                                                      i, 0, 1,
988                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
989                                                      scale, -scale, offx, offy,                                                      scale, -scale, offx, offy,
# Line 788  class MapCanvas(wxWindow, Publisher): Line 1024  class MapCanvas(wxWindow, Publisher):
1024          # to deselect the currently selected layer, so we simply select          # to deselect the currently selected layer, so we simply select
1025          # the already selected layer again.          # the already selected layer again.
1026          if layer is None:          if layer is None:
1027              layer = self.interactor.SelectedLayer()              layer = self.selection.SelectedLayer()
1028          self.interactor.SelectLayerAndShape(layer, shape)              shapes = []
1029            else:
1030                shapes = [shape]
1031            self.selection.SelectShapes(layer, shapes)
1032          return result          return result
1033    
1034      def LabelShapeAt(self, x, y):      def LabelShapeAt(self, x, y):
# Line 806  class MapCanvas(wxWindow, Publisher): Line 1045  class MapCanvas(wxWindow, Publisher):
1045              # a label was selected              # a label was selected
1046              label_layer.RemoveLabel(shape_index)              label_layer.RemoveLabel(shape_index)
1047          elif layer is not None:          elif layer is not None:
1048              text = labeldialog.run_label_dialog(self, layer.table, shape_index)              text = labeldialog.run_label_dialog(self,
1049                                                    layer.ShapeStore().Table(),
1050                                                    shape_index)
1051              if text:              if text:
1052                  proj = self.map.projection                  proj = self.map.projection
1053                  if proj is not None:                  if proj is not None:
# Line 821  class MapCanvas(wxWindow, Publisher): Line 1062  class MapCanvas(wxWindow, Publisher):
1062    
1063                  shapetype = layer.ShapeType()                  shapetype = layer.ShapeType()
1064                  if shapetype == SHAPETYPE_POLYGON:                  if shapetype == SHAPETYPE_POLYGON:
1065                      x, y = shape_centroid(layer.shapefile.cobject(),                      shapefile = layer.ShapeStore().Shapefile().cobject()
1066                                            shape_index,                      x, y = shape_centroid(shapefile, shape_index,
1067                                            map_proj, layer_proj, 1, 1, 0, 0)                                            map_proj, layer_proj, 1, 1, 0, 0)
1068                      if map_proj is not None:                      if map_proj is not None:
1069                          x, y = map_proj.Inverse(x, y)                          x, y = map_proj.Inverse(x, y)
# Line 847  class MapCanvas(wxWindow, Publisher): Line 1088  class MapCanvas(wxWindow, Publisher):
1088                      valign = ALIGN_CENTER                      valign = ALIGN_CENTER
1089                  label_layer.AddLabel(x, y, text,                  label_layer.AddLabel(x, y, text,
1090                                       halign = halign, valign = valign)                                       halign = halign, valign = valign)
1091    
1092    def OutputTransform(canvas_scale, canvas_offset, canvas_size, device_extend):
1093        """Calculate dimensions to transform canvas content to output device."""
1094        width, height = device_extend
1095    
1096        # Only 80 % of the with are available for the map
1097        width = width * 0.8
1098    
1099        # Define the distance of the map from DC border
1100        distance = 20
1101    
1102        if height < width:
1103            # landscape
1104            map_height = height - 2*distance
1105            map_width = map_height
1106        else:
1107            # portrait, recalibrate width (usually the legend width is too
1108            # small
1109            width = width * 0.9
1110            map_height = width - 2*distance
1111            map_width = map_height
1112        
1113        mapregion = (distance, distance,
1114                     distance+map_width, distance+map_height)
1115    
1116        canvas_width, canvas_height = canvas_size
1117        
1118        scalex = map_width / (canvas_width/canvas_scale)
1119        scaley = map_height / (canvas_height/canvas_scale)
1120        scale = min(scalex, scaley)
1121        canvas_offx, canvas_offy = canvas_offset
1122        offx = scale*canvas_offx/canvas_scale
1123        offy = scale*canvas_offy/canvas_scale
1124    
1125        return scale, (offx, offy), mapregion

Legend:
Removed from v.301  
changed lines
  Added in v.1285

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26