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

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

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

revision 76 by bh, Mon Feb 4 19:23:30 2002 UTC revision 1467 by jonathan, Tue Jul 22 14:02:57 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    # Jonathan Coles <[email protected]>
5    # Frank Koormann <[email protected]>
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
9    
10  __version__ = "$Revision$"  __version__ = "$Revision$"
11    
12  from wxPython.wx import wxPoint, wxColour, wxPen, wxBrush, wxFont, \  import cStringIO
      wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \  
      wxBLACK, wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL  
13    
14  from wxproj import draw_polygon_shape  from Thuban import _
15    
16  from Thuban.Model.layer import SHAPETYPE_POLYGON, SHAPETYPE_ARC, \  from wxPython.wx import wxMemoryDC, wxEmptyBitmap, \
17       SHAPETYPE_POINT      wxPoint, wxRect, wxPen, wxBrush, wxFont, \
18        wxTRANSPARENT_PEN, wxTRANSPARENT_BRUSH, \
19        wxBLACK_PEN, wxRED_PEN, wxBLACK, \
20        wxSOLID, wxCROSS_HATCH, wxSWISS, wxNORMAL, \
21        wxBitmap, wxImageFromBitmap, wxBitmapFromImage, \
22        wxImageFromStream, wxBITMAP_TYPE_BMP
23    
24    from wxproj import draw_polygon_shape, draw_polygon_init
25    
26    from Thuban.UI.common import Color2wxColour
27    from Thuban.UI.classifier import ClassDataPreviewer
28    from Thuban.UI.scalebar import ScaleBar
29    
30    from Thuban.Model.layer import Layer, RasterLayer, \
31         SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
32  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \  from Thuban.Model.label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \
33       ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE       ALIGN_LEFT, ALIGN_RIGHT, ALIGN_BASELINE
34    
35    from Thuban.Model.classification import Classification
36    from Thuban.Model.color import Transparent
37    import Thuban.Model.resource
38    
39    if Thuban.Model.resource.has_gdal_support():
40        from gdalwarp import ProjectRasterFile
41    
42  class MapRenderer:  class MapRenderer:
43    
# Line 41  class MapRenderer: Line 61  class MapRenderer:
61                  the renderer's default.                  the renderer's default.
62          """          """
63          # resolution in pixel/inch          # resolution in pixel/inch
64    
65          self.dc = dc          self.dc = dc
66          self.scale = scale          self.scale = scale
67          self.offset = offset          self.offset = offset
# Line 52  class MapRenderer: Line 73  class MapRenderer:
73    
74      def render_map(self, map):      def render_map(self, map):
75          self.map = map          self.map = map
76            seenRaster = True
77    
78            self.dc.BeginDrawing()
79    
80            #
81            # This is only a good optimization if there is only one
82            # raster layer and the image covers the entire window (as
83            # it currently does). We note if there is a raster layer
84            # and only begin drawing layers once we have drawn it.
85            # That way we avoid drawing layers that won't be seen.
86            #
87            if Thuban.Model.resource.has_gdal_support():
88                for layer in map.Layers():
89                    if isinstance(layer, RasterLayer) and layer.Visible():
90                        seenRaster = False
91                        break
92    
93          for layer in map.Layers():          for layer in map.Layers():
94              # if honor_visibility is true, only draw visible layers,              # if honor_visibility is true, only draw visible layers,
95              # otherwise draw all layers              # otherwise draw all layers
96              if not self.honor_visibility or layer.Visible():              if not self.honor_visibility or layer.Visible():
97                  self.draw_shape_layer(layer)                  if isinstance(layer, Layer) and seenRaster:
98                        self.draw_shape_layer(layer)
99                    elif isinstance(layer, RasterLayer) \
100                        and Thuban.Model.resource.has_gdal_support():
101                        self.draw_raster_layer(layer)
102                        seenRaster = True
103    
104          self.draw_label_layer(map.LabelLayer())          self.draw_label_layer(map.LabelLayer())
105    
106            self.dc.EndDrawing()
107    
108      def draw_shape_layer(self, layer):      def draw_shape_layer(self, layer):
109          scale = self.scale          scale = self.scale
110          offx, offy = self.offset          offx, offy = self.offset
111    
         fill = layer.fill  
         if fill is None:  
             brush = wxTRANSPARENT_BRUSH  
         else:  
             color = wxColour(fill.red * 255,  
                              fill.green * 255,  
                              fill.blue * 255)  
             brush = wxBrush(color, wxSOLID)  
         stroke = layer.stroke  
         stroke_width = layer.stroke_width  
         if stroke is None:  
             pen = wxTRANSPARENT_PEN  
         else:  
             color = wxColour(stroke.red * 255,  
                              stroke.green * 255,  
                              stroke.blue * 255)  
             pen = wxPen(color, stroke_width, wxSOLID)  
   
112          map_proj = self.map.projection          map_proj = self.map.projection
113          layer_proj = layer.projection          layer_proj = layer.projection
114    
115          shapetype = layer.ShapeType()          shapetype = layer.ShapeType()
116    
117          if shapetype == SHAPETYPE_POLYGON:          brush = wxTRANSPARENT_BRUSH
118              for i in range(layer.NumShapes()):          pen   = wxTRANSPARENT_PEN
119                  self.draw_polygon_shape(layer, i, pen, brush)  
120          else:          old_prop = None
121              self.dc.SetBrush(brush)          old_group = None
122              self.dc.SetPen(pen)          lc = layer.GetClassification()
123              if shapetype == SHAPETYPE_ARC:          field = layer.GetClassificationColumn()
124                  f = self.draw_arc_shape          defaultGroup = lc.GetDefaultGroup()
125              elif shapetype == SHAPETYPE_POINT:  
126                  f = self.draw_point_shape  
127              for i in range(layer.NumShapes()):  
128                  f(layer, i)          if shapetype == SHAPETYPE_POINT:
129                draw_func = self.draw_point_shape
130      def draw_polygon_shape(self, layer, index, pen, brush):              draw_func_param = layer
131          offx, offy = self.offset                  else:
132          draw_polygon_shape(layer.shapefile.cobject(), index,              draw_func = draw_polygon_shape
133                             self.dc, pen, brush,              draw_func_param = self.polygon_render_param(layer)
134                             self.map.projection, layer.projection,  
135                             self.scale, -self.scale, offx, offy)          table = layer.ShapeStore().Table()
136            for i in self.layer_ids(layer):
137    
138                if field is None:
139                    group = defaultGroup
140                else:
141                    record = table.ReadRowAsDict(i)
142                    assert record is not None
143                    group = lc.FindGroup(record[field])
144    
145    
146                if not group.IsVisible():
147                    continue
148    
149    
150                # don't recreate new objects if they are the same as before
151                if group is not old_group:
152                    old_group = group
153    
154                    prop = group.GetProperties()
155    
156                    if prop != old_prop:
157                        old_prop = prop
158    
159                        if shapetype == SHAPETYPE_ARC:
160                            fill = Transparent
161                        else:
162                            fill = prop.GetFill()
163    
164    
165                        if fill is Transparent:
166                            brush = wxTRANSPARENT_BRUSH
167                        else:
168                            color = Color2wxColour(fill)
169                            brush = wxBrush(color, wxSOLID)
170    
171                        stroke = prop.GetLineColor()
172                        stroke_width = prop.GetLineWidth()
173                        if stroke is Transparent:
174                            pen = wxTRANSPARENT_PEN
175                        else:
176                            color = Color2wxColour(stroke)
177                            pen = wxPen(color, stroke_width, wxSOLID)
178    
179                draw_func(draw_func_param, i, pen, brush)
180    
181        def draw_raster_layer(self, layer):
182            data = None
183            offx, offy = self.offset
184            width, height = self.dc.GetSizeTuple()
185    
186            inProj = ""
187            proj = layer.GetProjection()
188            if proj is not None:
189                for p in proj.GetAllParameters():
190                    inProj += "+" + p + " "
191    
192            outProj = ""
193            proj = self.map.GetProjection()
194            if proj is not None:
195                for p in proj.GetAllParameters():
196                    outProj += "+" + p + " "
197    
198            xmin = (0 - offx) / self.scale
199            ymin = (offy - height) / self.scale
200            xmax = (width - offx) / self.scale
201            ymax = (offy - 0) / self.scale
202    
203            try:
204                data = ProjectRasterFile(
205                    layer.GetImageFilename(),
206                    inProj,
207                    outProj,
208                    (xmin, ymin, xmax, ymax),
209                    "", (width, height))
210            except IOError, (strerr):
211                print strerr
212            except (AttributeError, ValueError):
213                pass
214            else:
215                if data is not None:
216                    stream = cStringIO.StringIO(data)
217                    image = wxImageFromStream(stream, wxBITMAP_TYPE_BMP)
218                    bitmap = wxBitmapFromImage(image)
219                    self.dc.DrawBitmap(bitmap, 0, 0)
220    
221        def layer_ids(self, layer):
222            """Return the shape ids of the given layer that have to be drawn.
223    
224            The default implementation simply returns all ids in the layer.
225            Override in derived classes to be more precise.
226            """
227            return range(layer.NumShapes())
228    
229        def polygon_render_param(self, layer):
230            """Return the low-lever render parameter for the layer"""
231            offx, offy = self.offset
232            return draw_polygon_init(layer.ShapeStore().Shapefile(), self.dc,
233                                     self.map.projection,
234                                     layer.projection,
235                                     self.scale, -self.scale,
236                                     offx, offy)
237    
238        def draw_polygon_shape(self, draw_polygon_info, index, pen, brush):
239            draw_polygon_shape(draw_polygon_info, index, pen, brush)
240    
241      def projected_points(self, layer, index):      def projected_points(self, layer, index):
242          proj = self.map.projection          proj = self.map.GetProjection()
243          if proj is not None:          if proj is not None:
244              forward = proj.Forward              forward = proj.Forward
245          else:          else:
246              forward = None              forward = None
247          proj = layer.projection          proj = layer.GetProjection()
248          if proj is not None:          if proj is not None:
249              inverse = proj.Inverse              inverse = proj.Inverse
250          else:          else:
# Line 134  class MapRenderer: Line 266  class MapRenderer:
266          points = self.projected_points(layer, index)          points = self.projected_points(layer, index)
267          self.dc.DrawLines(points)          self.dc.DrawLines(points)
268    
269      def draw_point_shape(self, layer, index):      def draw_point_shape(self, layer, index, pen, brush):
270          p = self.projected_points(layer, index)[0]          pp = self.projected_points(layer, index)
271    
272            if len(pp) == 0: return # ignore Null Shapes which have no points
273    
274            p = pp[0]
275          radius = self.resolution * 5          radius = self.resolution * 5
276            self.dc.SetBrush(brush)
277            self.dc.SetPen(pen)
278          self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)          self.dc.DrawEllipse(p.x - radius, p.y - radius, 2*radius, 2*radius)
279    
280      def draw_label_layer(self, layer):      def draw_label_layer(self, layer):
# Line 183  class ScreenRenderer(MapRenderer): Line 321  class ScreenRenderer(MapRenderer):
321      # On the screen we want to see only visible layers by default      # On the screen we want to see only visible layers by default
322      honor_visibility = 1      honor_visibility = 1
323            
324      def RenderMap(self, map, selected_layer, selected_shape):      def RenderMap(self, map, region, selected_layer, selected_shapes):
325            """Render the map.
326    
327            Only the given region (a tuple in window coordinates as returned
328            by a wxrect's asTuple method) needs to be redrawn. Highlight the
329            shapes given by the ids in selected_shapes in the
330            selected_layer.
331            """
332            self.update_region = region
333          self.selected_layer = selected_layer          self.selected_layer = selected_layer
334          self.selected_shape = selected_shape          self.selected_shapes = selected_shapes
335          self.render_map(map)          self.render_map(map)
336    
337      def draw_shape_layer(self, layer):      def draw_shape_layer(self, layer):
338          MapRenderer.draw_shape_layer(self, layer)          MapRenderer.draw_shape_layer(self, layer)
339          if layer is self.selected_layer and self.selected_shape is not None:          if layer is self.selected_layer and self.selected_shapes:
340              pen = wxPen(wxBLACK, 3, wxSOLID)              pen = wxPen(wxBLACK, 3, wxSOLID)
341              brush = wxBrush(wxBLACK, wxCROSS_HATCH)              brush = wxBrush(wxBLACK, wxCROSS_HATCH)
342                
343              shapetype = layer.ShapeType()              shapetype = layer.ShapeType()
             index = self.selected_shape  
344              if shapetype == SHAPETYPE_POLYGON:              if shapetype == SHAPETYPE_POLYGON:
345                  self.draw_polygon_shape(layer, index, pen, brush)                  offx, offy = self.offset
346              else:                  renderparam = self.polygon_render_param(layer)
347                    func = self.draw_polygon_shape
348                    args = (pen, brush)
349                elif shapetype == SHAPETYPE_ARC:
350                    renderparam = self.polygon_render_param(layer)
351                    func = self.draw_polygon_shape
352                    args = (pen, None)
353                elif shapetype == SHAPETYPE_POINT:
354                    renderparam = layer
355                  self.dc.SetBrush(brush)                  self.dc.SetBrush(brush)
356                  self.dc.SetPen(pen)                  self.dc.SetPen(pen)
357                  if shapetype == SHAPETYPE_ARC:                  func = self.draw_point_shape
358                      f = self.draw_arc_shape                  args = (pen, brush)
359                  elif shapetype == SHAPETYPE_POINT:              else:
360                      f = self.draw_point_shape                  raise TypeError(_("Unhandled shape type %s") % shapetype)
361                  f(layer, index)  
362                for index in self.selected_shapes:
363                    func(renderparam, index, *args)
364    
365        def layer_ids(self, layer):
366            """Return the shapeids covered by the region that has to be redrawn
367    
368            Call the layer's ShapesInRegion method to determine the ids so
369            that it can use the quadtree.
370            """
371            # FIXME: the quad-tree should be built from the projected
372            # coordinates not the lat-long ones because it's not trivial to
373            # determine an appropriate rectangle in lat-long for a given
374            # rectangle in projected coordinates which we have to start from
375            # here.
376            proj = self.map.projection
377            if proj is not None:
378                inverse = proj.Inverse
379            else:
380                inverse = None
381    
382            scale = self.scale
383            offx, offy = self.offset
384            xs = []
385            ys = []
386            x, y, width, height = self.update_region
387            for winx, winy in ((x, y), (x + width, y),
388                               (x + width, y + height), (x, y + height)):
389                px = (winx - offx) / scale
390                py = -(winy - offy) / scale
391                if inverse:
392                    px, py = inverse(px, py)
393                xs.append(px)
394                ys.append(py)
395            left = min(xs)
396            right = max(xs)
397            top = max(ys)
398            bottom = min(ys)
399    
400  class PrinterRender(MapRenderer):          return layer.ShapesInRegion((left, bottom, right, top))
401    
     # When printing we want to see all layers  
     honor_visibility = 0  
402    
403      RenderMap = MapRenderer.render_map  class ExportRenderer(ScreenRenderer):
404    
405        honor_visibility = 1
406            
407        def RenderMap(self, map, region, mapregion,
408                      selected_layer, selected_shapes ):
409            """Render the map.
410    
411            The rendering device has been specified during initialisation.
412            The device border distance was set in Thuban.UI.view.OutputTranform().
413            
414            RenderMap renders a frame set (one page frame, one around
415            legend/scalebar and one around the map), the map, the legend and the
416            scalebar on the given DC. The map is rendered with the region displayed
417            in the canvas view, centered on the area available for map display.
418            """
419            
420            self.update_region = region
421            self.selected_layer = selected_layer
422            self.selected_shapes = selected_shapes
423    
424            # Get some dimensions
425            llx, lly, urx, ury = region
426            self.mapregion = mapregion
427            mminx, mminy, mmaxx, mmaxy = self.mapregion
428    
429            # Manipulate the offset to position the map
430            offx, offy = self.offset
431            # 1. Shift to corner of map drawing area
432            offx = offx + mminx
433            offy = offy + mminy
434    
435            # 2. Center the map on the map drawing area:
436            # region identifies the region on the canvas view:
437            # center of map drawing area - half the size of region: rendering origin
438            self.shiftx = (mmaxx - mminx)*0.5 - (urx - llx)*0.5
439            self.shifty = (mmaxy - mminy)*0.5 - (ury - lly)*0.5
440    
441            self.offset = (offx+self.shiftx, offy+self.shifty)
442    
443            # Draw the map
444            self.dc.BeginDrawing()
445            self.dc.DestroyClippingRegion()
446            self.dc.SetClippingRegion(mminx+self.shiftx, mminy+self.shifty,
447                                      urx, ury)
448            self.render_map(map)
449            self.dc.EndDrawing()
450    
451            # Draw the rest (frames, legend, scalebar)
452            self.dc.BeginDrawing()
453            self.dc.DestroyClippingRegion()
454    
455            # Force the font for Legend drawing
456            font = wxFont(self.resolution * 10, wxSWISS, wxNORMAL, wxNORMAL)
457            self.dc.SetFont(font)
458    
459            self.render_frame(region)
460            self.render_legend(map)
461            self.render_scalebar(map)
462            self.dc.EndDrawing()
463    
464        def render_frame(self, region):
465            """Render the frames for map and legend/scalebar."""
466    
467            dc = self.dc
468            dc.SetPen(wxBLACK_PEN)
469            dc.SetBrush(wxTRANSPARENT_BRUSH)
470    
471            # Dimension stuff
472            width, height = dc.GetSizeTuple()
473            mminx, mminy, mmaxx, mmaxy = self.mapregion
474    
475            # Page Frame
476            dc.DrawRectangle(15,15,width-30, (mmaxy-mminy)+10)
477            
478            # Map Frame
479            llx, lly, urx, ury = region
480            dc.DrawRectangle(mminx + self.shiftx, mminy + self.shifty, urx, ury)
481            
482            # Legend Frame
483            dc.DrawRectangle(mmaxx+10,mminy,(width-20) - (mmaxx+10), mmaxy-mminy)
484    
485            dc.DestroyClippingRegion()
486            dc.SetClippingRegion(mmaxx+10,mminy,
487                                 (width-20) - (mmaxx+10), mmaxy-mminy)
488    
489        def render_legend(self, map):
490            """Render the legend on the Map."""
491    
492            previewer = ClassDataPreviewer()
493            dc = self.dc
494            dc.SetPen(wxBLACK_PEN)
495            dc.SetBrush(wxTRANSPARENT_BRUSH)
496    
497            # Dimension stuff
498            width, height = dc.GetSizeTuple()
499            mminx, mminy, mmaxx, mmaxy = self.mapregion
500            textwidth, textheight = dc.GetTextExtent("0")
501            iconwidth  = textheight
502            iconheight = textheight
503            stepy = textheight+3
504            dx = 10
505            posx = mmaxx + 10 + 5   # 10 pix distance mapframe/legend frame,
506                                    # 5 pix inside legend frame
507            posy = mminy + 5        # 5 pix inside legend frame
508            
509            # Render the legend
510            dc.SetTextForeground(wxBLACK)
511            if map.HasLayers():
512                layers = map.Layers()
513                layers.reverse()
514                for l in layers:
515                    if l.Visible():
516                        # Render title
517                        dc.DrawText(l.Title(), posx, posy)
518                        posy+=stepy
519                        if l.HasClassification():
520                            # Render classification
521                            clazz = l.GetClassification()
522                            shapeType = l.ShapeType()
523                            for g in clazz:
524                                if g.IsVisible():
525                                    previewer.Draw(dc,
526                                        wxRect(posx+dx, posy,
527                                               iconwidth, iconheight),
528                                        g.GetProperties(), shapeType)
529                                    dc.DrawText(g.GetDisplayText(),
530                                                posx+2*dx+iconwidth, posy)
531                                    posy+=stepy
532            
533        def render_scalebar(self, map):
534            """Render the scalebar."""
535    
536            scalebar = ScaleBar(map)
537    
538            # Dimension stuff
539            width, height = self.dc.GetSizeTuple()
540            mminx, mminy, mmaxx, mmaxy = self.mapregion
541          
542            # Render the scalebar
543            scalebar.DrawScaleBar(self.scale, self.dc,
544                                 (mmaxx+10+5, mmaxy-25),
545                                 ((width-15-5) - (mmaxx+10+5),20)
546                                )
547            # 10 pix between map and legend frame, 5 pix inside legend frame
548            # 25 pix from the legend frame bottom line
549            # Width: 15 pix from DC border, 5 pix inside frame, 10, 5 as above
550            # Height: 20
551    
552    class PrinterRenderer(ExportRenderer):
553    
554        # Printing as well as Export / Screen display only the visible layer.
555        honor_visibility = 1
556    

Legend:
Removed from v.76  
changed lines
  Added in v.1467

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26