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

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26