Experimental patch for sizeable point symbols

Jan-Oliver Wagner jan at intevation.de
Thu Sep 23 00:57:35 CEST 2004


Hi,

attached is a patch (against current HEAD) and a sample file
that adds a size for point symbols.

It is experimental. You have to edit the .thuban file
manually to have symbols of different size.
I am also not sure if it is the best technical approach to solve this.
But I would be glad if we could as a first step agree on
whether the dtd definition is OK and can be checked in
with the sample file.

Note: I wrote this patch in the train from Düsseldorf
to Osnabrück (1h) plus some fiddling tonight with the
wxGrid to adapt to the size of the symbol.

Best

	Jan

-- 
Jan-Oliver Wagner               http://intevation.de/~jan/

Intevation GmbH                      http://intevation.de/
FreeGIS                                http://freegis.org/
-------------- next part --------------
? build
? thubaninit.py
? Data/iceland_sample_size.thuban
? test/temp
Index: Resources/XML/thuban-1.1.dtd
===================================================================
RCS file: /thubanrepository/thuban/Resources/XML/thuban-1.1.dtd,v
retrieving revision 1.1
diff -u -3 -p -r1.1 thuban-1.1.dtd
--- Resources/XML/thuban-1.1.dtd	12 Mar 2004 12:19:15 -0000	1.1
+++ Resources/XML/thuban-1.1.dtd	22 Sep 2004 22:45:12 -0000
@@ -10,7 +10,7 @@
     Read the file COPYING coming with Thuban for details.
 
     $Revision: 1.1 $
-    $Source: /thubanrepository/thuban/Resources/XML/thuban-1.1.dtd,v $
+    $Source: /home/thuban/jail/thubanrepository/thuban/Resources/XML/thuban-1.1.dtd,v $
     $Id: thuban-1.1.dtd,v 1.1 2004/03/12 12:19:15 bh Exp $
 -->
 
@@ -153,12 +153,13 @@ identify the version of the file format.
 <!-- the fill and stroke attributes can be either "None" or "#RRGGBB"
      RGB hex values
 
-     All of fill, stroke and stroke_width may be omitted and default to
-     "None", "#000000" and "1" respectively.
+     All of fill, stroke, stroke_width and size may be omitted and default
+     to "None", "#000000", "1" and "5" respectively.
  -->
 <!ATTLIST layer fill CDATA "None">
 <!ATTLIST layer stroke CDATA "#000000">
 <!ATTLIST layer stroke_width CDATA "1">
+<!ATTLIST layer size CDATA "5">
 
 
 <!-- a rasterlayer represents an image that has some geographic data
@@ -201,14 +202,15 @@ identify the version of the file format.
      the fill and stroke attributes can be either "None" or "#RRGGBB"
      RGB hex values
 
-     All of fill, stroke and stroke_width may be omitted and default to
-     "None", "#000000" and "1" respectively.
+     All of fill, stroke, stroke_width and size may be omitted and default to
+     "None", "#000000", "1" and "5" respectively.
  -->
 <!ELEMENT cldata EMPTY>
 <!ATTLIST cldata 
         stroke       CDATA #IMPLIED
         stroke_width CDATA #IMPLIED
         fill         CDATA #IMPLIED
+        size         CDATA #IMPLIED
 >
 
 
Index: Thuban/Model/classification.py
===================================================================
RCS file: /thubanrepository/thuban/Thuban/Model/classification.py,v
retrieving revision 1.38
diff -u -3 -p -r1.38 classification.py
--- Thuban/Model/classification.py	3 Nov 2003 13:55:41 -0000	1.38
+++ Thuban/Model/classification.py	22 Sep 2004 22:45:12 -0000
@@ -315,7 +315,7 @@ class Classification(Publisher):
             items.append(build_item(p, p.GetDisplayText()))
 
         return (_("Classification"), items)
- 
+
 class ClassIterator:
     """Allows the Groups in a Classifcation to be interated over.
 
@@ -327,11 +327,11 @@ class ClassIterator:
         """Constructor.
 
         default -- the default group
- 
+
         points -- a list of singleton groups
 
         ranges -- a list of range groups
- 
+
         maps -- a list of map groups
         """
 
@@ -350,10 +350,10 @@ class ClassIterator:
             d = self.data[self.data_index]
             self.data_index += 1
             return d
-        
+
 class ClassGroupProperties:
     """Represents the properties of a single Classification Group.
-  
+
     These are used when rendering a layer."""
 
     def __init__(self, props = None):
@@ -370,6 +370,7 @@ class ClassGroupProperties:
         else:
             self.SetLineColor(Black)
             self.SetLineWidth(1)
+            self.SetSize(5)
             self.SetFill(Transparent)
 
     def SetProperties(self, props):
@@ -378,8 +379,9 @@ class ClassGroupProperties:
         assert isinstance(props, ClassGroupProperties)
         self.SetLineColor(props.GetLineColor())
         self.SetLineWidth(props.GetLineWidth())
+        self.SetSize(props.GetSize())
         self.SetFill(props.GetFill())
-        
+
     def GetLineColor(self):
         """Return the line color as a Color object."""
         return self.__stroke
@@ -407,10 +409,25 @@ class ClassGroupProperties:
 
         self.__strokeWidth = lineWidth
 
+    def GetSize(self):
+        """Return the size."""
+        return self.__size
+
+    def SetSize(self, size):
+        """Set the size.
+
+        size -- the new size. This must be > 0.
+        """
+        assert isinstance(size, types.IntType)
+        if (size < 1):
+            raise ValueError(_("size < 1"))
+
+        self.__size = size
+
     def GetFill(self):
         """Return the fill color as a Color object."""
         return self.__fill
- 
+
     def SetFill(self, fill):
         """Set the fill color.
 
@@ -431,7 +448,8 @@ class ClassGroupProperties:
                  self.__stroke == other.__stroke)        \
             and (self.__fill is other.__fill or          \
                  self.__fill == other.__fill)            \
-            and self.__strokeWidth == other.__strokeWidth
+            and self.__strokeWidth == other.__strokeWidth\
+            and self.__size == other.__size
 
     def __ne__(self, other): 
         return not self.__eq__(other)
@@ -443,7 +461,8 @@ class ClassGroupProperties:
         return ClassGroupProperties(self)
 
     def __repr__(self):
-        return repr((self.__stroke, self.__strokeWidth, self.__fill))
+        return repr((self.__stroke, self.__strokeWidth, self.__size,
+                    self.__fill))
 
 class ClassGroup:
     """A base class for all Groups within a Classification"""
@@ -466,7 +485,7 @@ class ClassGroup:
     def GetLabel(self):
         """Return the Group's label."""
         return self.label
- 
+
     def SetLabel(self, label):
         """Set the Group's label.
 
Index: Thuban/Model/load.py
===================================================================
RCS file: /thubanrepository/thuban/Thuban/Model/load.py,v
retrieving revision 1.48
diff -u -3 -p -r1.48 load.py
--- Thuban/Model/load.py	12 Mar 2004 12:19:15 -0000	1.48
+++ Thuban/Model/load.py	22 Sep 2004 22:45:12 -0000
@@ -554,6 +554,7 @@ class SessionLoader(XMLReader):
             parse_color(attrs.get((None, 'stroke'), "None")))
         self.cl_prop.SetLineWidth(
             int(attrs.get((None, 'stroke_width'), "0")))
+        self.cl_prop.SetSize(int(attrs.get((None, 'size'), "5")))
         self.cl_prop.SetFill(parse_color(attrs.get((None, 'fill'), "None")))
 
     def end_cldata(self, name, qname):
Index: Thuban/UI/baserenderer.py
===================================================================
RCS file: /thubanrepository/thuban/Thuban/UI/baserenderer.py,v
retrieving revision 1.11
diff -u -3 -p -r1.11 baserenderer.py
--- Thuban/UI/baserenderer.py	13 Jul 2004 11:07:45 -0000	1.11
+++ Thuban/UI/baserenderer.py	22 Sep 2004 22:45:12 -0000
@@ -17,7 +17,7 @@ renderers.
 from __future__ import generators
 
 __version__ = "$Revision: 1.11 $"
-# $Source: /thubanrepository/thuban/Thuban/UI/baserenderer.py,v $
+# $Source: /home/thuban/jail/thubanrepository/thuban/Thuban/UI/baserenderer.py,v $
 # $Id: baserenderer.py,v 1.11 2004/07/13 11:07:45 bh Exp $
 
 import sys
@@ -289,7 +289,10 @@ class BaseRenderer:
                 data = shape.RawData()
             else:
                 data = shape.Points()
-            draw_func(draw_func_param, data, pen, brush)
+            if draw_func == self.draw_point_shape:
+                 draw_func(draw_func_param, data, pen, brush, size = group.GetProperties().GetSize())
+            else:
+                 draw_func(draw_func_param, data, pen, brush)
             if count % 500 == 0:
                 yield True
 
@@ -421,7 +424,7 @@ class BaseRenderer:
         for part in points:
             self.dc.DrawLines(part)
 
-    def draw_point_shape(self, layer, points, pen, brush):
+    def draw_point_shape(self, layer, points, pen, brush, size = 5):
         """Draw a point shape from layer with the given brush and pen
 
         The shape is given by points argument which is a the return
@@ -435,7 +438,7 @@ class BaseRenderer:
         if not points:
             return
 
-        radius = int(round(self.resolution * 5))
+        radius = int(round(self.resolution * size))
         self.dc.SetBrush(brush)
         self.dc.SetPen(pen)
         for part in points:
Index: Thuban/UI/classifier.py
===================================================================
RCS file: /thubanrepository/thuban/Thuban/UI/classifier.py,v
retrieving revision 1.65
diff -u -3 -p -r1.65 classifier.py
--- Thuban/UI/classifier.py	7 May 2004 20:20:43 -0000	1.65
+++ Thuban/UI/classifier.py	22 Sep 2004 22:45:12 -0000
@@ -331,7 +331,7 @@ class ClassTable(wxPyGridTableBase):
 
         row = -1
         self.clazz = clazz
- 
+
         self.__NotifyRowChanges(old_len, self.GetNumberRows())
 
         #
@@ -439,11 +439,11 @@ class ClassTable(wxPyGridTableBase):
         """
 
         self.SetValueAsCustom(row, col, None, value)
-       
+
     def GetValueAsCustom(self, row, col, typeName):
         """Return the object that is used to represent the given
            cell coordinates. This may not be a string.
- 
+
         typeName -- unused, but needed to overload wxPyGridTableBase
         """
 
@@ -915,13 +915,13 @@ class Classifier(NonModalNonParentDialog
             table.SetValueAsCustom(row, COL_SYMBOL, None, new_prop)
         self.Enable(True)
         propDlg.Destroy()
-        
+
     def _SetClassification(self, clazz):
         """Called from the ClassGen dialog when a new classification has
         been created and should be set in the table.
         """
         # FIXME: This could be implemented using a message
-        
+
         self.fields.SetClientData(self.__cur_field, clazz)
         self.classGrid.GetTable().SetClassification(clazz)
 
@@ -1266,10 +1266,10 @@ class SelectPropertiesDialog(wxDialog):
         topBox.Add(buttonBox, 0, wxALIGN_RIGHT|wxBOTTOM|wxTOP, 10)
 
         button_ok.SetDefault()
-                                                                                
+
         #EVT_BUTTON(self, wxID_OK, self._OnOK)
         #EVT_BUTTON(self, ID_SELPROP_CANCEL, self._OnCancel)
-                                                                                
+
         self.SetAutoLayout(True)
         self.SetSizer(topBox)
         topBox.Fit(self)
@@ -1296,7 +1296,7 @@ class SelectPropertiesDialog(wxDialog):
         dialog.Destroy()
 
         return ret
-        
+
     def _OnChangeLineColor(self, event):
         clr = self.__GetColor(self.prop.GetLineColor())
         if clr is not None:
@@ -1306,7 +1306,7 @@ class SelectPropertiesDialog(wxDialog):
     def _OnChangeLineColorTrans(self, event):
         self.prop.SetLineColor(Transparent)
         self.previewWin.Refresh() # XXX: work around, see ClassDataPreviewer
-        
+
     def _OnChangeFillColor(self, event):
         clr = self.__GetColor(self.prop.GetFill())
         if clr is not None:
@@ -1360,6 +1360,13 @@ class ClassDataPreviewer:
     """Class that actually draws a group property preview."""
 
     def Draw(self, dc, rect, prop, shapeType):
+        """Draw the property.
+
+        returns: (w, h) as adapted extend if the drawing size
+        exceeded the given rect. This can only be the case
+        for point symbols. If the symbol fits the given rect,
+        None is returned.
+        """
 
         assert dc is not None
         assert isinstance(prop, ClassGroupProperties)
@@ -1399,12 +1406,20 @@ class ClassDataPreviewer:
 
         elif shapeType == SHAPETYPE_POINT:
 
-            dc.DrawCircle(x + w/2, y + h/2,
-                          (min(w, h) - prop.GetLineWidth())/2)
+            dc.DrawCircle(x + w/2, y + h/2, prop.GetSize())
+            circle_size =  prop.GetSize() * 2 + prop.GetLineWidth() * 2
+            new_h = h
+            new_w = w
+            if h < circle_size: new_h = circle_size
+            if w < circle_size: new_w = circle_size
+            if new_h > h or new_w > w:
+                return (new_w, new_h)
 
         elif shapeType == SHAPETYPE_POLYGON:
             dc.DrawRectangle(x, y, w, h)
 
+        return None
+
 class ClassRenderer(wxPyGridCellRenderer):
     """A wrapper class that can be used to draw group properties in a
     grid table.
@@ -1426,7 +1441,26 @@ class ClassRenderer(wxPyGridCellRenderer
                          rect.GetWidth(), rect.GetHeight())
 
         if not isinstance(data, ClassGroupMap):
-            self.previewer.Draw(dc, rect, data.GetProperties(), self.shapeType)
+            new_size = self.previewer.Draw(dc, rect, data.GetProperties(),
+                                           self.shapeType)
+            if new_size is not None:
+                (new_w, new_h) = new_size
+                grid.SetRowSize(row, new_h)
+                grid.SetColSize(col, new_h)
+                grid.ForceRefresh()
+
+                # now that we know the height, redraw everything
+                rect.SetHeight(new_h)
+                rect.SetWidth(new_w)
+                dc.DestroyClippingRegion()
+                dc.SetClippingRegion(rect.GetX(), rect.GetY(), 
+                                     rect.GetWidth(), rect.GetHeight())
+                dc.SetPen(wxPen(wxLIGHT_GREY))
+                dc.SetBrush(wxBrush(wxLIGHT_GREY, wxSOLID))
+                dc.DrawRectangle(rect.GetX(), rect.GetY(), 
+                                 rect.GetWidth(), rect.GetHeight())
+                self.previewer.Draw(dc, rect, data.GetProperties(),
+                                    self.shapeType)
 
         if isSelected:
             dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
Index: test/test_load.py
===================================================================
RCS file: /thubanrepository/thuban/test/test_load.py,v
retrieving revision 1.38
diff -u -3 -p -r1.38 test_load.py
--- test/test_load.py	12 Mar 2004 12:19:15 -0000	1.38
+++ test/test_load.py	22 Sep 2004 22:45:12 -0000
@@ -162,6 +162,8 @@ class ClassificationTest(LoadSessionTest
                 props.SetLineWidth(data[CLASSES][i][GROUP_PROPS][1])
                 props.SetFill(
                     parse_color(data[CLASSES][i][GROUP_PROPS][2]))
+                if len(data[CLASSES][i][GROUP_PROPS]) > 3:
+                    props.SetSize(data[CLASSES][i][GROUP_PROPS][3])
 
                 if data[CLASSES][i][GROUP_TYPE] == "default":
                     g = ClassGroupDefault(props, data[CLASSES][i][GROUP_LABEL])
@@ -384,6 +386,53 @@ class TestLayerVisibility(LoadSessionTes
         layer = layers[0]
 
         eq(layer.Visible(), False)
+
+        self.check_format()
+
+
+class TestSymbolSize(ClassificationTest):
+
+    file_contents = '''\
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE session SYSTEM "thuban-1.1.dtd">
+<session xmlns="http://thuban.intevation.org/dtds/thuban-1.1-dev.dtd" title="Thuban sample session">
+    <fileshapesource filetype="shapefile" id="D813968480" filename="../../Data/iceland/cultural_landmark-point.shp"/>
+    <map title="Iceland map">
+        <layer title="cultural_landmark-point" stroke_width="1" shapestore="D813968480" visible="true" stroke="#000000" fill="#000000">
+            <classification field="CLPTLABEL" field_type="string">
+                <clnull label="">
+                    <cldata stroke="#000000" stroke_width="1" size="3" fill="#000000"/>
+                </clnull>
+                <clpoint label="" value="RUINS">
+                    <cldata stroke="#000000" stroke_width="1" size="6" fill="#ffffff"/>
+                </clpoint>
+                <clpoint label="" value="FARM">
+                    <cldata stroke="#000000" stroke_width="1" size="9" fill="#ffff00"/>
+                </clpoint>
+            </classification>
+        </layer>
+    </map>
+</session>
+'''
+
+    def test(self):
+        """Test that the size definition for point symbols is correctly
+        loaded for a layer."""
+        eq = self.assertEquals
+        session = load_session(self.filename())
+        self.session = session
+
+        map = session.Maps()[0] # only one map in the sample
+
+        expected = [("cultural_landmark-point", 2,
+                        [("default", (), "",
+                            ("#000000", 1, "#000000", 3)),
+                         ("single", "RUINS", "",
+                            ("#000000", 1, "#ffffff", 6)),
+                         ("single", "FARM", "",
+                            ("#000000", 1, "#ffff00", 9))])]
+
+        self.TestLayers(map.Layers(), expected)
 
         self.check_format()
 
-------------- next part --------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE session SYSTEM "thuban.dtd">
<session title="Thuban sample session">
    <map title="Iceland map">
        <projection>
            <parameter value="zone=26"/>
            <parameter value="proj=utm"/>
            <parameter value="ellps=clrk66"/>
        </projection>
        <layer stroke="#000000" filename="iceland/political.shp" fill="#c0c0c0" stroke_width="1" title="political"/>
        <layer stroke="#ffffff" filename="iceland/roads-line.shp" fill="None" stroke_width="1" title="roads-line">
            <classification field="RDLNTYPE" field_type="int">
                <clnull>
                    <cldata stroke="#ffffff" stroke_width="1" fill="None"/>
                </clnull>
                <clpoint value="1">
                    <cldata stroke="#ff0000" stroke_width="1" fill="None"/>
                </clpoint>
                <clpoint value="2">
                    <cldata stroke="#00aa00" stroke_width="1" fill="None"/>
                </clpoint>
                <clpoint value="3">
                    <cldata stroke="#0000ff" stroke_width="1" fill="None"/>
                </clpoint>
            </classification>
        </layer>
        <layer stroke="#000000" filename="iceland/cultural_landmark-point.shp" fill="#000000" stroke_width="1" title="cultural_landmark-point">
            <classification field="CLPTLABEL" field_type="string">
                <clnull>
                    <cldata stroke="#000000" stroke_width="1" size="4" fill="#000000"/>
                </clnull>
                <clpoint value="RUINS">
                    <cldata stroke="#000000" stroke_width="1" size="8" fill="#ffffff"/>
                </clpoint>
                <clpoint value="FARM">
                    <cldata stroke="#000000" stroke_width="1" size="20" fill="#ffff00"/>
                </clpoint>
            </classification>
        </layer>
    </map>
</session>


More information about the Thuban-devel mailing list

This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)