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

Legend:
Removed from v.6  
changed lines
  Added in v.1298

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26