/[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 303 by bh, Mon Sep 2 16:47:53 2002 UTC revision 1271 by jonathan, Fri Jun 20 16:43:04 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, wxYield,\
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, wxBeginBusyCursor, wxEndBusyCursor, wxCursor, \
26         wxImageFromBitmap, wxPlatform
27    
28    # Export related stuff
29    if wxPlatform == '__WXMSW__':
30        from wxPython.wx import wxMetaFileDC
31    from wxPython.wx import wxFileDialog, wxSAVE, wxOVERWRITE_PROMPT, wxID_OK
32    
33  from wxPython import wx  from wxPython import wx
34    
35  from wxproj import point_in_polygon_shape, shape_centroid  from wxproj import point_in_polygon_shape, shape_centroid
36    
   
37  from Thuban.Model.messages import MAP_PROJECTION_CHANGED, \  from Thuban.Model.messages import MAP_PROJECTION_CHANGED, \
38       LAYERS_CHANGED, LAYER_LEGEND_CHANGED, LAYER_VISIBILITY_CHANGED       LAYER_PROJECTION_CHANGED, \
39         MAP_LAYERS_CHANGED, LAYER_CHANGED, LAYER_VISIBILITY_CHANGED
40  from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \  from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \
41       SHAPETYPE_POINT       SHAPETYPE_POINT
42  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
43       ALIGN_LEFT, ALIGN_RIGHT       ALIGN_LEFT, ALIGN_RIGHT
44  from Thuban.Lib.connector import Publisher  from Thuban.Lib.connector import Publisher
45    from Thuban.Model.color import Color
46    
47    import resource
48    
49  from renderer import ScreenRenderer, PrinterRender  from selection import Selection
50    from renderer import ScreenRenderer, ExportRenderer, PrinterRenderer
51    
52  import labeldialog  import labeldialog
53    
54  from messages import SELECTED_SHAPE, VIEW_POSITION  from messages import LAYER_SELECTED, SHAPES_SELECTED, VIEW_POSITION, \
55                         SCALE_CHANGED
56    
57    
58  #  #
# Line 215  class LabelTool(Tool): Line 232  class LabelTool(Tool):
232          self.view.LabelShapeAt(event.m_x, event.m_y)          self.view.LabelShapeAt(event.m_x, event.m_y)
233    
234    
   
   
235  class MapPrintout(wx.wxPrintout):  class MapPrintout(wx.wxPrintout):
236    
237      """      """
238      wxPrintout class for printing Thuban maps      wxPrintout class for printing Thuban maps
239      """      """
240    
241      def __init__(self, map):      def __init__(self, canvas, map, region, selected_layer, selected_shapes):
242          wx.wxPrintout.__init__(self)          wx.wxPrintout.__init__(self)
243            self.canvas = canvas
244          self.map = map          self.map = map
245            self.region = region
246            self.selected_layer = selected_layer
247            self.selected_shapes = selected_shapes
248    
249      def GetPageInfo(self):      def GetPageInfo(self):
250          return (1, 1, 1, 1)          return (1, 1, 1, 1)
# Line 239  class MapPrintout(wx.wxPrintout): Line 258  class MapPrintout(wx.wxPrintout):
258    
259      def draw_on_dc(self, dc):      def draw_on_dc(self, dc):
260          width, height = self.GetPageSizePixels()          width, height = self.GetPageSizePixels()
261          llx, lly, urx, ury = self.map.ProjectedBoundingBox()          scale, offset, mapregion = OutputTransform(self.canvas.scale,
262          scalex = width / (urx - llx)                                                     self.canvas.offset,
263          scaley = height / (ury - lly)                                                     self.canvas.GetSizeTuple(),
264          scale = min(scalex, scaley)                                                     self.GetPageSizePixels())
         offx = 0.5 * (width - (urx + llx) * scale)  
         offy = 0.5 * (height + (ury + lly) * scale)  
   
265          resx, resy = self.GetPPIPrinter()          resx, resy = self.GetPPIPrinter()
266          renderer = PrinterRender(dc, scale, (offx, offy), resolution = resx)          renderer = PrinterRenderer(dc, scale, offset, resolution = resy)
267          renderer.RenderMap(self.map)          x, y, width, height = self.region
268          return wx.true          canvas_scale = self.canvas.scale
269            renderer.RenderMap(self.map,
270                               (0,0,
271                                    (width/canvas_scale)*scale,
272                                    (height/canvas_scale)*scale),
273                                    mapregion,
274                               self.selected_layer, self.selected_shapes)
275            return True
276    
277  class MapCanvas(wxWindow, Publisher):  class MapCanvas(wxWindow, Publisher):
278    
279      """A widget that displays a map and offers some interaction"""      """A widget that displays a map and offers some interaction"""
280    
281      def __init__(self, parent, winid, interactor):      # Some messages that can be subscribed/unsubscribed directly through
282        # the MapCanvas come in fact from other objects. This is a dict
283        # mapping those messages to the names of the instance variables they
284        # actually come from. The delegation is implemented in the Subscribe
285        # and Unsubscribe methods
286        delegated_messages = {LAYER_SELECTED: "selection",
287                              SHAPES_SELECTED: "selection"}
288    
289        # Methods delegated to some instance variables. The delegation is
290        # implemented in the __getattr__ method.
291        delegated_methods = {"SelectLayer": "selection",
292                             "SelectShapes": "selection",
293                             "SelectedLayer": "selection",
294                             "HasSelectedLayer": "selection",
295                             "HasSelectedShapes": "selection",
296                             "SelectedShapes": "selection"}
297    
298        def __init__(self, parent, winid):
299          wxWindow.__init__(self, parent, winid)          wxWindow.__init__(self, parent, winid)
300          self.SetBackgroundColour(wxColour(255, 255, 255))          self.SetBackgroundColour(wxColour(255, 255, 255))
301    
302          # the map displayed in this canvas. Set with SetMap()          # the map displayed in this canvas. Set with SetMap()
303          self.map = None          self.map = None
304    
305            # current map projection. should only differ from map.projection
306            # when the map's projection is changing and we need access to the
307            # old projection.
308            self.current_map_proj = None
309    
310          # scale and offset describe the transformation from projected          # scale and offset describe the transformation from projected
311          # coordinates to window coordinates.          # coordinates to window coordinates.
312          self.scale = 1.0          self.scale = 1.0
# Line 282  class MapCanvas(wxWindow, Publisher): Line 326  class MapCanvas(wxWindow, Publisher):
326          # the bitmap serving as backing store          # the bitmap serving as backing store
327          self.bitmap = None          self.bitmap = None
328    
329          # the interactor          # the selection
330          self.interactor = interactor          self.selection = Selection()
331          self.interactor.Subscribe(SELECTED_SHAPE, self.shape_selected)          self.selection.Subscribe(SHAPES_SELECTED , self.shape_selected)
332    
333          # keep track of which layers/shapes are selected to make sure we          # keep track of which layers/shapes are selected to make sure we
334          # only redraw when necessary          # only redraw when necessary
# Line 303  class MapCanvas(wxWindow, Publisher): Line 347  class MapCanvas(wxWindow, Publisher):
347          wxWindow.__del__(self)          wxWindow.__del__(self)
348          Publisher.__del__(self)          Publisher.__del__(self)
349    
350        def Subscribe(self, channel, *args):
351            """Extend the inherited method to handle delegated messages.
352    
353            If channel is one of the delegated messages call the appropriate
354            object's Subscribe method. Otherwise just call the inherited
355            method.
356            """
357            if channel in self.delegated_messages:
358                object = getattr(self, self.delegated_messages[channel])
359                object.Subscribe(channel, *args)
360            else:
361                Publisher.Subscribe(self, channel, *args)
362    
363        def Unsubscribe(self, channel, *args):
364            """Extend the inherited method to handle delegated messages.
365    
366            If channel is one of the delegated messages call the appropriate
367            object's Unsubscribe method. Otherwise just call the inherited
368            method.
369            """
370            if channel in self.delegated_messages:
371                object = getattr(self, self.delegated_messages[channel])
372                object.Unsubscribe(channel, *args)
373            else:
374                Publisher.Unsubscribe(self, channel, *args)
375    
376        def __getattr__(self, attr):
377            if attr in self.delegated_methods:
378                return getattr(getattr(self, self.delegated_methods[attr]), attr)
379            raise AttributeError(attr)
380    
381      def OnPaint(self, event):      def OnPaint(self, event):
382          dc = wxPaintDC(self)          dc = wxPaintDC(self)
383          if self.map is not None and self.map.HasLayers():          clear = self.map is None or not self.map.HasLayers()
             self.do_redraw()  
         else:  
             # If we've got no map or if the map is empty, simply clear  
             # the screen.  
384    
385              # XXX it's probably possible to get rid of this. The          wxBeginBusyCursor()
386              # background color of the window is already white and the          wxYield()
387              # only thing we may have to do is to call self.Refresh()  
388              # with a true argument in the right places.          try:
389              dc.BeginDrawing()              if not clear:
390              dc.Clear()                  self.do_redraw()
391              dc.EndDrawing()                  try:
392                        pass
393                    except:
394                        print "Error during drawing:", sys.exc_info()[0]
395                        clear = True
396    
397                if clear:
398                    # If we've got no map or if the map is empty, simply clear
399                    # the screen.
400    
401                    # XXX it's probably possible to get rid of this. The
402                    # background color of the window is already white and the
403                    # only thing we may have to do is to call self.Refresh()
404                    # with a true argument in the right places.
405                    dc.BeginDrawing()
406                    dc.Clear()
407                    dc.EndDrawing()
408            finally:
409                wxEndBusyCursor()
410    
411      def do_redraw(self):      def do_redraw(self):
412          # 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 335  class MapCanvas(wxWindow, Publisher): Line 424  class MapCanvas(wxWindow, Publisher):
424              dc.BeginDrawing()              dc.BeginDrawing()
425    
426              # clear the background              # clear the background
427              dc.SetBrush(wx.wxWHITE_BRUSH)              #dc.SetBrush(wx.wxWHITE_BRUSH)
428              dc.SetPen(wx.wxTRANSPARENT_PEN)              #dc.SetPen(wx.wxTRANSPARENT_PEN)
429              dc.DrawRectangle(0, 0, width, height)              #dc.DrawRectangle(0, 0, width, height)
430                dc.SetBackground(wx.wxWHITE_BRUSH)
431              if 1: #self.interactor.selected_map is self.map:              dc.Clear()
432                  selected_layer = self.interactor.selected_layer  
433                  selected_shape = self.interactor.selected_shape              selected_layer = self.selection.SelectedLayer()
434              else:              selected_shapes = self.selection.SelectedShapes()
                 selected_layer = None  
                 selected_shape = None  
435    
436              # draw the map into the bitmap              # draw the map into the bitmap
437              renderer = ScreenRenderer(dc, self.scale, self.offset)              renderer = ScreenRenderer(dc, self.scale, self.offset)
# Line 352  class MapCanvas(wxWindow, Publisher): Line 439  class MapCanvas(wxWindow, Publisher):
439              # Pass the entire bitmap as update region to the renderer.              # Pass the entire bitmap as update region to the renderer.
440              # We're redrawing the whole bitmap, after all.              # We're redrawing the whole bitmap, after all.
441              renderer.RenderMap(self.map, (0, 0, width, height),              renderer.RenderMap(self.map, (0, 0, width, height),
442                                 selected_layer, selected_shape)                                 selected_layer, selected_shapes)
443    
444              dc.EndDrawing()              dc.EndDrawing()
445              dc.SelectObject(wx.wxNullBitmap)              dc.SelectObject(wx.wxNullBitmap)
# Line 366  class MapCanvas(wxWindow, Publisher): Line 453  class MapCanvas(wxWindow, Publisher):
453          clientdc.Blit(0, 0, width, height, dc, 0, 0)          clientdc.Blit(0, 0, width, height, dc, 0, 0)
454          clientdc.EndDrawing()          clientdc.EndDrawing()
455    
456        def Export(self):
457            if self.scale == 0:
458                return
459    
460            if hasattr(self, "export_path"):
461                export_path = self.export_path
462            else:
463                export_path="."
464            dlg = wxFileDialog(self, _("Export Map"), export_path, "",
465                               "Enhanced Metafile (*.wmf)|*.wmf",
466                               wxSAVE|wxOVERWRITE_PROMPT)
467            if dlg.ShowModal() == wxID_OK:
468                self.export_path = os.path.dirname(dlg.GetPath())
469                dc = wxMetaFileDC(dlg.GetPath())
470        
471                scale, offset, mapregion = OutputTransform(self.scale,
472                                                           self.offset,
473                                                           self.GetSizeTuple(),
474                                                           dc.GetSizeTuple())
475    
476                selected_layer = self.selection.SelectedLayer()
477                selected_shapes = self.selection.SelectedShapes()
478    
479                renderer = ExportRenderer(dc, scale, offset)
480    
481                # Pass the entire bitmap as update region to the renderer.
482                # We're redrawing the whole bitmap, after all.
483                width, height = self.GetSizeTuple()
484                renderer.RenderMap(self.map,
485                                    (0,0,
486                                        (width/self.scale)*scale,
487                                        (height/self.scale)*scale),
488                                    mapregion,
489                                    selected_layer, selected_shapes)
490                dc.EndDrawing()
491                dc.Close()
492            dlg.Destroy()
493            
494      def Print(self):      def Print(self):
495          printer = wx.wxPrinter()          printer = wx.wxPrinter()
496          printout = MapPrintout(self.map)          width, height = self.GetSizeTuple()
497          printer.Print(self, printout, wx.true)          selected_layer = self.selection.SelectedLayer()
498            selected_shapes = self.selection.SelectedShapes()
499            
500            printout = MapPrintout(self, self.map, (0, 0, width, height),
501                                   selected_layer, selected_shapes)
502            printer.Print(self, printout, True)
503          printout.Destroy()          printout.Destroy()
504    
505      def SetMap(self, map):      def SetMap(self, map):
506          redraw_channels = (LAYERS_CHANGED, LAYER_LEGEND_CHANGED,          redraw_channels = (MAP_LAYERS_CHANGED, LAYER_CHANGED,
507                             LAYER_VISIBILITY_CHANGED)                             LAYER_VISIBILITY_CHANGED)
508          if self.map is not None:          if self.map is not None:
509              for channel in redraw_channels:              for channel in redraw_channels:
510                  self.map.Unsubscribe(channel, self.full_redraw)                  self.map.Unsubscribe(channel, self.full_redraw)
511              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,              self.map.Unsubscribe(MAP_PROJECTION_CHANGED,
512                                   self.projection_changed)                                   self.map_projection_changed)
513                self.map.Unsubscribe(LAYER_PROJECTION_CHANGED,
514                                     self.layer_projection_changed)
515          self.map = map          self.map = map
516            self.current_map_proj = self.map.GetProjection()
517            self.selection.ClearSelection()
518          if self.map is not None:          if self.map is not None:
519              for channel in redraw_channels:              for channel in redraw_channels:
520                  self.map.Subscribe(channel, self.full_redraw)                  self.map.Subscribe(channel, self.full_redraw)
521              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.projection_changed)              self.map.Subscribe(MAP_PROJECTION_CHANGED, self.map_projection_changed)
522                self.map.Subscribe(LAYER_PROJECTION_CHANGED, self.layer_projection_changed)
523          self.FitMapToWindow()          self.FitMapToWindow()
524          # 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
525          # 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 402  class MapCanvas(wxWindow, Publisher): Line 537  class MapCanvas(wxWindow, Publisher):
537          self.bitmap = None          self.bitmap = None
538          self.redraw()          self.redraw()
539    
540      def projection_changed(self, *args):      def map_projection_changed(self, *args):
541          self.FitMapToWindow()  
542            proj = self.current_map_proj
543            self.current_map_proj = self.map.GetProjection()
544    
545            bbox = None
546    
547            if proj is not None and self.current_map_proj is not None:
548                width, height = self.GetSizeTuple()
549                llx, lly = self.win_to_proj(0, height)
550                urx, ury = self.win_to_proj(width, 0)
551                bbox = proj.Inverse(llx, lly) + proj.Inverse(urx, ury)
552                bbox = self.current_map_proj.ForwardBBox(bbox)
553    
554            if bbox is not None:
555                self.FitRectToWindow(bbox)
556            else:
557                self.FitMapToWindow()
558    
559            self.full_redraw()
560    
561        def layer_projection_changed(self, *args):
562          self.full_redraw()          self.full_redraw()
563    
564      def set_view_transform(self, scale, offset):      def set_view_transform(self, scale, offset):
565            # width/height of the projected bbox
566            llx, lly, urx, ury = bbox = self.map.ProjectedBoundingBox()
567            pwidth = float(urx - llx)
568            pheight = float(ury - lly)
569    
570            # width/height of the window
571            wwidth, wheight = self.GetSizeTuple()
572    
573            # The window's center in projected coordinates assuming the new
574            # scale/offset
575            pcenterx = (wwidth/2 - offset[0]) / scale
576            pcentery = (offset[1] - wheight/2) / scale
577    
578            # The window coordinates used when drawing the shapes must fit
579            # into 16bit signed integers.
580            max_len = max(pwidth, pheight)
581            if max_len:
582                max_scale = 32000.0 / max_len
583            else:
584                # FIXME: What to do in this case? The bbox is effectively
585                # empty so any scale should work.
586                max_scale = scale
587    
588            # The minimal scale is somewhat arbitrarily set to half that of
589            # the bbox fit into the window
590            scales = []
591            if pwidth:
592                scales.append(wwidth / pwidth)
593            if pheight:
594                scales.append(wheight / pheight)
595            if scales:
596                min_scale = 0.5 * min(scales)
597            else:
598                min_scale = scale
599    
600            if scale > max_scale:
601                scale = max_scale
602            elif scale < min_scale:
603                scale = min_scale
604    
605          self.scale = scale          self.scale = scale
606          self.offset = offset  
607            # determine new offset to preserve the center
608            self.offset = (wwidth/2 - scale * pcenterx,
609                           wheight/2 + scale * pcentery)
610          self.full_redraw()          self.full_redraw()
611            self.issue(SCALE_CHANGED, scale)
612    
613      def proj_to_win(self, x, y):      def proj_to_win(self, x, y):
614          """\          """\
615          Return the point in  window coords given by projected coordinates x y          Return the point in  window coords given by projected coordinates x y
616          """          """
617            if self.scale == 0:
618                return (0, 0)
619    
620          offx, offy = self.offset          offx, offy = self.offset
621          return (self.scale * x + offx, -self.scale * y + offy)          return (self.scale * x + offx, -self.scale * y + offy)
622    
# Line 422  class MapCanvas(wxWindow, Publisher): Line 624  class MapCanvas(wxWindow, Publisher):
624          """\          """\
625          Return the point in projected coordinates given by window coords x y          Return the point in projected coordinates given by window coords x y
626          """          """
627            if self.scale == 0:
628                return (0, 0)
629    
630          offx, offy = self.offset          offx, offy = self.offset
631          return ((x - offx) / self.scale, (offy - y) / self.scale)          return ((x - offx) / self.scale, (offy - y) / self.scale)
632    
633      def FitRectToWindow(self, rect):      def FitRectToWindow(self, rect):
634          """Fit the rectangular region given by rect into the window.          """Fit the rectangular region given by rect into the window.
635            
636          Set scale so that rect (in projected coordinates) just fits into          Set scale so that rect (in projected coordinates) just fits into
637          the window and center it.          the window and center it.
638          """          """
639          width, height = self.GetSizeTuple()          width, height = self.GetSizeTuple()
640          llx, lly, urx, ury = rect          llx, lly, urx, ury = rect
641          if llx == urx or lly == ury:          if llx == urx or lly == ury:
642              # zero with or zero height. Do Nothing              # zero width or zero height. Do Nothing
643              return              return
644          scalex = width / (urx - llx)          scalex = width / (urx - llx)
645          scaley = height / (ury - lly)          scaley = height / (ury - lly)
# Line 445  class MapCanvas(wxWindow, Publisher): Line 650  class MapCanvas(wxWindow, Publisher):
650    
651      def FitMapToWindow(self):      def FitMapToWindow(self):
652          """Fit the map to the window          """Fit the map to the window
653            
654          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
655          center it in the window.          center it in the window.
656          """          """
657          bbox = self.map.ProjectedBoundingBox()          if self.map is not None:
658                bbox = self.map.ProjectedBoundingBox()
659                if bbox is not None:
660                    self.FitRectToWindow(bbox)
661    
662        def FitLayerToWindow(self, layer):
663            """Fit the given layer to the window.
664    
665            Set the scale so that the layer fits exactly into the window and
666            center it in the window.
667            """
668            
669            bbox = layer.LatLongBoundingBox()
670          if bbox is not None:          if bbox is not None:
671              self.FitRectToWindow(bbox)              proj = self.map.GetProjection()
672                if proj is not None:
673                    bbox = proj.ForwardBBox(bbox)
674    
675                if bbox is not None:
676                    self.FitRectToWindow(bbox)
677    
678        def FitSelectedToWindow(self):
679            layer = self.selection.SelectedLayer()
680            shapes = self.selection.SelectedShapes()
681    
682            bbox = layer.ShapesBoundingBox(shapes)
683            if bbox is not None:
684                proj = self.map.GetProjection()
685                if proj is not None:
686                    bbox = proj.ForwardBBox(bbox)
687    
688                if bbox is not None:
689                    if len(shapes) == 1 and layer.ShapeType() == SHAPETYPE_POINT:
690                        self.ZoomFactor(1, self.proj_to_win(bbox[0], bbox[1]))
691                    else:
692                        self.FitRectToWindow(bbox)
693    
694      def ZoomFactor(self, factor, center = None):      def ZoomFactor(self, factor, center = None):
695          """Multiply the zoom by factor and center on center.          """Multiply the zoom by factor and center on center.
# Line 460  class MapCanvas(wxWindow, Publisher): Line 698  class MapCanvas(wxWindow, Publisher):
698          that should be centered. If it is omitted, it defaults to the          that should be centered. If it is omitted, it defaults to the
699          center of the window          center of the window
700          """          """
701          width, height = self.GetSizeTuple()          if self.scale > 0:
702          scale = self.scale * factor              width, height = self.GetSizeTuple()
703          offx, offy = self.offset              scale = self.scale * factor
704          if center is not None:              offx, offy = self.offset
705              cx, cy = center              if center is not None:
706          else:                  cx, cy = center
707              cx = width / 2              else:
708              cy = height / 2                  cx = width / 2
709          offset = (factor * (offx - cx) + width / 2,                  cy = height / 2
710                    factor * (offy - cy) + height / 2)              offset = (factor * (offx - cx) + width / 2,
711          self.set_view_transform(scale, offset)                      factor * (offy - cy) + height / 2)
712                self.set_view_transform(scale, offset)
713    
714      def ZoomOutToRect(self, rect):      def ZoomOutToRect(self, rect):
715          """Zoom out to fit the currently visible region into rect.          """Zoom out to fit the currently visible region into rect.
# Line 497  class MapCanvas(wxWindow, Publisher): Line 736  class MapCanvas(wxWindow, Publisher):
736          offx, offy = self.offset          offx, offy = self.offset
737          self.set_view_transform(self.scale, (offx + dx, offy + dy))          self.set_view_transform(self.scale, (offx + dx, offy + dy))
738    
739        def SelectTool(self, tool):
740            """Make tool the active tool.
741    
742            The parameter should be an instance of Tool or None to indicate
743            that no tool is active.
744            """
745            self.tool = tool
746    
747      def ZoomInTool(self):      def ZoomInTool(self):
748          """Start the zoom in tool"""          """Start the zoom in tool"""
749          self.tool = ZoomInTool(self)          self.SelectTool(ZoomInTool(self))
750    
751      def ZoomOutTool(self):      def ZoomOutTool(self):
752          """Start the zoom out tool"""          """Start the zoom out tool"""
753          self.tool = ZoomOutTool(self)          self.SelectTool(ZoomOutTool(self))
754    
755      def PanTool(self):      def PanTool(self):
756          """Start the pan tool"""          """Start the pan tool"""
757          self.tool = PanTool(self)          self.SelectTool(PanTool(self))
758            #img = resource.GetImageResource("pan", wxBITMAP_TYPE_XPM)
759            #bmp = resource.GetBitmapResource("pan", wxBITMAP_TYPE_XPM)
760            #print bmp
761            #img = wxImageFromBitmap(bmp)
762            #print img
763            #cur = wxCursor(img)
764            #print cur
765            #self.SetCursor(cur)
766    
767      def IdentifyTool(self):      def IdentifyTool(self):
768          """Start the identify tool"""          """Start the identify tool"""
769          self.tool = IdentifyTool(self)          self.SelectTool(IdentifyTool(self))
770    
771      def LabelTool(self):      def LabelTool(self):
772          """Start the label tool"""          """Start the label tool"""
773          self.tool = LabelTool(self)          self.SelectTool(LabelTool(self))
774    
775      def CurrentTool(self):      def CurrentTool(self):
776          """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 561  class MapCanvas(wxWindow, Publisher): Line 816  class MapCanvas(wxWindow, Publisher):
816          self.set_current_position(event)          self.set_current_position(event)
817          if self.dragging:          if self.dragging:
818              self.ReleaseMouse()              self.ReleaseMouse()
819              self.tool.Hide(self.drag_dc)              try:
820              self.tool.MouseUp(event)                  self.tool.Hide(self.drag_dc)
821              self.drag_dc = None                  self.tool.MouseUp(event)
822          self.dragging = 0              finally:
823                    self.drag_dc = None
824                    self.dragging = 0
825    
826      def OnMotion(self, event):      def OnMotion(self, event):
827          self.set_current_position(event)          self.set_current_position(event)
# Line 584  class MapCanvas(wxWindow, Publisher): Line 841  class MapCanvas(wxWindow, Publisher):
841          # Even when the window becomes larger some parts of the bitmap          # Even when the window becomes larger some parts of the bitmap
842          # could be reused.          # could be reused.
843          self.full_redraw()          self.full_redraw()
844            pass
845    
846      def shape_selected(self, layer, shape):      def shape_selected(self, layer, shape):
847          """Redraw the map.          """Receiver for the SHAPES_SELECTED messages. Redraw the map."""
848            # The selection object takes care that it only issues
849          Receiver for the SELECTED_SHAPE messages. Try to redraw only          # SHAPES_SELECTED messages when the set of selected shapes has
850          when necessary.          # actually changed, so we can do a full redraw unconditionally.
851          """          # FIXME: We should perhaps try to limit the redraw to the are
852          # A redraw is necessary when the display has to change, which          # actually covered by the shapes before and after the selection
853          # means that either the status changes from having no selection          # change.
854          # 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  
855    
856      def unprojected_rect_around_point(self, x, y, dist):      def unprojected_rect_around_point(self, x, y, dist):
857          """return a rect dist pixels around (x, y) in unprojected corrdinates          """return a rect dist pixels around (x, y) in unprojected corrdinates
# Line 648  class MapCanvas(wxWindow, Publisher): Line 895  class MapCanvas(wxWindow, Publisher):
895              forward = None              forward = None
896    
897          scale = self.scale          scale = self.scale
898    
899            if scale == 0:
900                return None, None
901    
902          offx, offy = self.offset          offx, offy = self.offset
903    
904          if select_labels:          if select_labels:
# Line 696  class MapCanvas(wxWindow, Publisher): Line 947  class MapCanvas(wxWindow, Publisher):
947              if not layer.Visible():              if not layer.Visible():
948                  continue                  continue
949    
950              filled = layer.fill is not None              filled = layer.GetClassification().GetDefaultFill() \
951              stroked = layer.stroke is not None                       is not Color.Transparent
952                stroked = layer.GetClassification().GetDefaultLineColor() \
953                          is not Color.Transparent
954    
955              layer_proj = layer.projection              layer_proj = layer.projection
956              if layer_proj is not None:              if layer_proj is not None:
# Line 724  class MapCanvas(wxWindow, Publisher): Line 977  class MapCanvas(wxWindow, Publisher):
977    
978              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
979                  for i in shape_ids:                  for i in shape_ids:
980                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      shapefile = layer.ShapeStore().Shapefile().cobject()
981                                                      i,                      result = point_in_polygon_shape(shapefile, i,
982                                                      filled, stroked,                                                      filled, stroked,
983                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
984                                                      scale, -scale, offx, offy,                                                      scale, -scale, offx, offy,
# Line 735  class MapCanvas(wxWindow, Publisher): Line 988  class MapCanvas(wxWindow, Publisher):
988                          break                          break
989              elif shapetype == SHAPETYPE_ARC:              elif shapetype == SHAPETYPE_ARC:
990                  for i in shape_ids:                  for i in shape_ids:
991                      result = point_in_polygon_shape(layer.shapefile.cobject(),                      shapefile = layer.ShapeStore().Shapefile().cobject()
992                        result = point_in_polygon_shape(shapefile,
993                                                      i, 0, 1,                                                      i, 0, 1,
994                                                      map_proj, layer_proj,                                                      map_proj, layer_proj,
995                                                      scale, -scale, offx, offy,                                                      scale, -scale, offx, offy,
# Line 776  class MapCanvas(wxWindow, Publisher): Line 1030  class MapCanvas(wxWindow, Publisher):
1030          # to deselect the currently selected layer, so we simply select          # to deselect the currently selected layer, so we simply select
1031          # the already selected layer again.          # the already selected layer again.
1032          if layer is None:          if layer is None:
1033              layer = self.interactor.SelectedLayer()              layer = self.selection.SelectedLayer()
1034          self.interactor.SelectLayerAndShape(layer, shape)              shapes = []
1035            else:
1036                shapes = [shape]
1037            self.selection.SelectShapes(layer, shapes)
1038          return result          return result
1039    
1040      def LabelShapeAt(self, x, y):      def LabelShapeAt(self, x, y):
# Line 794  class MapCanvas(wxWindow, Publisher): Line 1051  class MapCanvas(wxWindow, Publisher):
1051              # a label was selected              # a label was selected
1052              label_layer.RemoveLabel(shape_index)              label_layer.RemoveLabel(shape_index)
1053          elif layer is not None:          elif layer is not None:
1054              text = labeldialog.run_label_dialog(self, layer.table, shape_index)              text = labeldialog.run_label_dialog(self,
1055                                                    layer.ShapeStore().Table(),
1056                                                    shape_index)
1057              if text:              if text:
1058                  proj = self.map.projection                  proj = self.map.projection
1059                  if proj is not None:                  if proj is not None:
# Line 809  class MapCanvas(wxWindow, Publisher): Line 1068  class MapCanvas(wxWindow, Publisher):
1068    
1069                  shapetype = layer.ShapeType()                  shapetype = layer.ShapeType()
1070                  if shapetype == SHAPETYPE_POLYGON:                  if shapetype == SHAPETYPE_POLYGON:
1071                      x, y = shape_centroid(layer.shapefile.cobject(),                      shapefile = layer.ShapeStore().Shapefile().cobject()
1072                                            shape_index,                      x, y = shape_centroid(shapefile, shape_index,
1073                                            map_proj, layer_proj, 1, 1, 0, 0)                                            map_proj, layer_proj, 1, 1, 0, 0)
1074                      if map_proj is not None:                      if map_proj is not None:
1075                          x, y = map_proj.Inverse(x, y)                          x, y = map_proj.Inverse(x, y)
# Line 835  class MapCanvas(wxWindow, Publisher): Line 1094  class MapCanvas(wxWindow, Publisher):
1094                      valign = ALIGN_CENTER                      valign = ALIGN_CENTER
1095                  label_layer.AddLabel(x, y, text,                  label_layer.AddLabel(x, y, text,
1096                                       halign = halign, valign = valign)                                       halign = halign, valign = valign)
1097    
1098    def OutputTransform(canvas_scale, canvas_offset, canvas_size, device_extend):
1099        """Calculate dimensions to transform canvas content to output device."""
1100        width, height = device_extend
1101    
1102        # Only 80 % of the with are available for the map
1103        width = width * 0.8
1104    
1105        # Define the distance of the map from DC border
1106        distance = 20
1107    
1108        if height < width:
1109            # landscape
1110            map_height = height - 2*distance
1111            map_width = map_height
1112        else:
1113            # portrait, recalibrate width (usually the legend width is too
1114            # small
1115            width = width * 0.9
1116            map_height = width - 2*distance
1117            map_width = map_height
1118        
1119        mapregion = (distance, distance,
1120                     distance+map_width, distance+map_height)
1121    
1122        canvas_width, canvas_height = canvas_size
1123        
1124        scalex = map_width / (canvas_width/canvas_scale)
1125        scaley = map_height / (canvas_height/canvas_scale)
1126        scale = min(scalex, scaley)
1127        canvas_offx, canvas_offy = canvas_offset
1128        offx = scale*canvas_offx/canvas_scale
1129        offy = scale*canvas_offy/canvas_scale
1130    
1131        return scale, (offx, offy), mapregion

Legend:
Removed from v.303  
changed lines
  Added in v.1271

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26