[OpenLayers-Dev] text rendering support in openlayers for wfs layers

dave c dc at openapp.ie
Mon Nov 5 14:30:14 EST 2007


I posted on this last month but i didnt get any response so i went ahead 
anyways. Now what i put together is only a first pass of sorts and is a 
little crude in places but i was hoping that someone here could have a look 
at it and tell me if it could be used within openlayers as i dont want to 
have to continually be updating it myself. Of course you may not like the way 
i went about it or have no plans to implement text rendering.

Using openlayers 2.5 stable, I created a patch for this version if anyone 
wants to have a  look. The patch is called patch.txt and is attached to this 
mail. So run "patch -p0 < ../patch.txt" to update.

Just a few quick points and issues i had.

One reason to get text rendering working for example was, i needed to be able 
to output the names of certain hospitals on a map centered on there point on 
the map. Firstly i have "extractAttributes: true" and added an layer option 
called  "textAttrToDisplay" which is the name of the attribute to be 
displayed within each feature that was coming from my database.

Now biggest issue i had was in svg the text was rendering upside down due to 
the "transform scale(1,-1)" and so i just created a 2nd root node called 
textroot which i appended to the renderedRoot and added all text nodes to it 
instead. Of course this wasn't an issue in vml. Is there a better way ?

I tried finding a quick example that you could quickly test this out on 
yourselves, but all the wfs examples i looked at in openlayers did not return 
any other data except there co-ordinates, but i assume a number of ye would 
be able to get it running quickly with your own local datasets.

So as an example of its use, say this is some of the data returned from my 
database, 
<gml:featureMember>
	<fs:hospital fid="1">
	<fs:geometry>
	<gml:Point>
<gml:coordinates>99771.29,47991.03</gml:coordinates>
</gml:Point>
</fs:geometry>
<fs:hospname>Bantry</fs:hospname>
<fs:county>Co Cork</fs:county>
<fs:he_admin_area>South</fs:he_admin_area>
<fs:address>Bantry</fs:address>
<fs:recordno>21</fs:recordno>
<fs:regionname>Region 2</fs:regionname>
</fs:hospital>
</gml:featureMember>

And i wanted to display the hospital name, i would add textAttrToDisplay as an 
option to the layer with the value hospname.

var lOptions = {
extractAttributes: true,
textAttrToDisplay : 'hospname'
}

layer2 = new 
OpenLayers.Layer.WFS( "Hospital", "http://localhost/~dave/featureserver/featureserver.cgi/hospital?format=WFS", 
{ maxfeatures:10},  lOptions);
map.addLayer(layer2);


Just looking for a bit of advice on this. Am i going about it the right way,  
and could i eventually submit this, so i wouldn't have to maintain it with 
with each update to openlayers itself.

Thanks 
-------------- next part --------------
Index: lib/OpenLayers/Renderer.js
===================================================================
--- lib/OpenLayers/Renderer.js	(.../tags/2.5)	(revision 8852)
+++ lib/OpenLayers/Renderer.js	(.../branches/textnode)	(working copy)
@@ -135,17 +135,39 @@
      *
      * Parameters:
      * feature - {<OpenLayers.Feature.Vector>} 
-     * style - {<Object>} 
+     * style - {<Object>}
+     * attr - {String}
      */
-    drawFeature: function(feature, style) {
+    drawFeature: function(feature, style, attr) {
         if(style == null) {
             style = feature.style;
         }
         this.drawGeometry(feature.geometry, style, feature.id);
+
+        //check if an text attribute name has been passed in the wfs layer options
+        var attrName = feature.layer.options.textAttrToDisplay
+        //if so and extractAttributes is true then render the text attributer associated with that geometry
+        if(feature.layer.options.extractAttributes==true && feature.attributes[attrName])
+        {
+         style = OpenLayers.Feature.Vector.style['text'];
+         featuretext = feature.attributes[attrName];
+         this.drawGeometryText(feature.geometry, style, feature.id, featuretext);
+        }
     },
 
 
     /** 
+     * Method: drawGeometryText
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} 
+     * style - {Object} 
+     * featureId - {<String>}
+     * featuretext - {<String>}
+     */
+    drawGeometryText: function(geometry, style, featureId,featuretext) {},
+
+    /** 
      * Method: drawGeometry
      * 
      * Draw a geometry.  This should only be called from the renderer itself.
Index: lib/OpenLayers/Feature/Vector.js
===================================================================
--- lib/OpenLayers/Feature/Vector.js	(.../tags/2.5)	(revision 8852)
+++ lib/OpenLayers/Feature/Vector.js	(.../branches/textnode)	(working copy)
@@ -318,5 +318,8 @@
         hoverPointRadius: 1,
         hoverPointUnit: "%",
         pointerEvents: "visiblePainted"
+    },
+    'text': {
+        fillColor: "red"
     }
 };    
Index: lib/OpenLayers/Renderer/Elements.js
===================================================================
--- lib/OpenLayers/Renderer/Elements.js	(.../tags/2.5)	(revision 8852)
+++ lib/OpenLayers/Renderer/Elements.js	(.../branches/textnode)	(working copy)
@@ -34,6 +34,12 @@
     root: null,
 
     /**
+     * Property: textroot
+     * {DOMElement}
+     */
+    textroot: null,
+
+    /**
      * Property: xmlns
      * {String}
      */    
@@ -50,7 +56,8 @@
 
         this.rendererRoot = this.createRenderRoot();
         this.root = this.createRoot();
-        
+        //calls either a svg or vml function to add the textroot node to the renderRoot node
+        this.appendTextToRoot();
         this.rendererRoot.appendChild(this.root);
         this.container.appendChild(this.rendererRoot);
     },
@@ -64,6 +71,7 @@
 
         this.rendererRoot = null;
         this.root = null;
+        this.textroot = null;
         this.xmlns = null;
 
         OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
@@ -78,6 +86,11 @@
             while (this.root.childNodes.length > 0) {
                 this.root.removeChild(this.root.firstChild);
             }
+          }
+        if (this.textroot) {
+            while (this.textroot.childNodes.length > 0) {
+                this.textroot.removeChild(this.textroot.firstChild);
+            }
         }
     },
 
@@ -98,8 +111,47 @@
      */
     getNodeType: function(geometry) { },
 
+    /**
+     * Method: appendTextToNode
+     *
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    appendTextToNode: function(geometry,node) { },
+
+    /**
+     * Method: appendTextToRoot
+     *
+     */
+    appendTextToRoot: function() { },
+
     /** 
      * Method: drawGeometry 
+     * Draw the text, creating new nodes, setting paths, setting style,
+     *     setting featureId on the node.  This method should only be called
+     *     by the renderer itself.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * featureId - {String}
+     * featuretext - {String}
+     */
+    drawGeometryText: function(geometry, style, featureId, featuretext) {
+        geometry.CLASS_NAME = "OpenLayers.Geometry.Text";
+        var nodeType = this.getNodeType(geometry);
+        var node = this.nodeFactory(geometry.id+"Text", nodeType, geometry);
+        node._featureId = featureId;
+        node._geometryClass = geometry.CLASS_NAME;
+        node._style = style;
+        node.textContent = featuretext;
+        this.appendTextToNode(geometry,node);
+        this.drawGeometryNode(node, geometry);
+    },
+
+    /** 
+     * Method: drawGeometry 
      * Draw the geometry, creating new nodes, setting paths, setting style,
      *     setting featureId on the node.  This method should only be called
      *     by the renderer itself.
@@ -152,6 +204,9 @@
             'isStroked': true
         };
         switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Text":
+                this.drawText(node, geometry);
+                break;
             case "OpenLayers.Geometry.Point":
                 this.drawPoint(node, geometry);
                 break;
@@ -196,6 +251,15 @@
     drawPoint: function(node, geometry) {},
 
     /**
+     * Method: drawText
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     */ 
+    drawText: function(node, geometry) {},
+
+    /**
      * Method: drawLineString
      * Virtual function for drawing LineString Geometry. 
      * Should be implemented by subclasses.
Index: lib/OpenLayers/Renderer/VML.js
===================================================================
--- lib/OpenLayers/Renderer/VML.js	(.../tags/2.5)	(revision 8852)
+++ lib/OpenLayers/Renderer/VML.js	(.../branches/textnode)	(working copy)
@@ -103,6 +103,24 @@
     },
 
     /**
+     * Method: appendTextToNode
+     * this function adds the text node to the the point data node,
+     * whereas the vml function adds the text node to the textroot node
+     */
+    appendTextToNode: function(geometry,node) {
+        var element = OpenLayers.Util.getElement(geometry.id);
+        element.appendChild(node);
+    },
+
+    /**
+     * Method: appendTextToRoot
+     * the vml dosnot need a textroot node whereas,
+     * the svg adds the textroot node to the rendererRoot node,
+     */
+    appendTextToRoot: function() {
+    },
+
+    /**
      * Method: getNodeType
      * Get the noode type for a geometry
      *
@@ -115,6 +133,9 @@
     getNodeType: function(geometry) {
         var nodeType = null;
         switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Text":
+                nodeType = "v:textbox";
+                break;
             case "OpenLayers.Geometry.Point":
                 nodeType = "v:oval";
                 break;
@@ -150,6 +171,10 @@
         style = style  || node._style;
         options = options || node._options;
         
+        if (node._geometryClass == "OpenLayers.Geometry.Text") {
+        //dosnt work, tried centering text in IE
+        node.style.textAlign = "center";
+        }
         if (node._geometryClass == "OpenLayers.Geometry.Point") {
             if (style.externalGraphic) {
                 // remove old node
@@ -378,6 +403,18 @@
     },
 
     /**
+     * Method: drawText
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    drawText: function(node, geometry) {
+        this.drawTextLine(node, geometry);
+    },
+
+    /**
      * Method: drawCircle
      * Render a circle.
      * Size and Center a circle given geometry (x,y center) and radius
@@ -390,10 +427,9 @@
     drawCircle: function(node, geometry, radius) {
         if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
             var resolution = this.getResolution();
-        
+
             node.style.left = (geometry.x /resolution).toFixed() - radius;
             node.style.top = (geometry.y /resolution).toFixed() - radius;
-    
             var diameter = radius * 2;
             
             node.style.width = diameter;
@@ -401,6 +437,19 @@
         }
     },
 
+    /**
+     * Method: drawTextLine
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    drawTextLine: function(node, geometry) {
+        if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
+           node.innerHTML = node.textContent;
+        }
+    },
 
     /**
      * Method: drawLineString
Index: lib/OpenLayers/Renderer/SVG.js
===================================================================
--- lib/OpenLayers/Renderer/SVG.js	(.../tags/2.5)	(revision 8852)
+++ lib/OpenLayers/Renderer/SVG.js	(.../branches/textnode)	(working copy)
@@ -139,6 +139,9 @@
     getNodeType: function(geometry) {
         var nodeType = null;
         switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Text":
+                nodeType = "text";
+                break;
             case "OpenLayers.Geometry.Point":
                 nodeType = "circle";
                 break;
@@ -180,6 +183,10 @@
         style = style  || node._style;
         options = options || node._options;
 
+        if (node._geometryClass == "OpenLayers.Geometry.Text") {
+        //centering text on the point
+        node.setAttributeNS(null, "style", "text-anchor: middle");
+        }
         if (node._geometryClass == "OpenLayers.Geometry.Point") {
             if (style.externalGraphic) {
                 // remove old node
@@ -311,6 +318,43 @@
         return root;
     },
 
+    /**
+     * Method: createTextRoot
+     * 
+     * Returns:
+     * {DOMElement} The textroot element to which we'll add text data
+     * This i just added for svg because i was unable to output the 
+     * text data in the main root element without the text displaying 
+     * upside down, due to the transform scale(1,-1), which is added 
+     * to the main root element
+     */
+    createTextRoot: function() {
+        var id = this.container.id + "_textroot";
+
+        var textroot = this.nodeFactory(id, "g");
+
+        return textroot;
+    },
+
+    /**
+     * Method: appendTextToNode
+     * this function adds the text node to the textroot node,
+     * whereas the vml function adds the text node to the the point data node
+     */
+    appendTextToNode: function(geometry,node) {
+        this.textroot.appendChild(node);
+    },
+
+    /**
+     * Method: appendTextToRoot
+     * this function adds the textroot node to the rendererRoot node,
+     * whereas the vml dosnot need a textroot node
+     */
+    appendTextToRoot: function() {
+        this.textroot = this.createTextRoot();
+        this.rendererRoot.appendChild(this.textroot);
+    },
+
     /**************************************
      *                                    *
      *     GEOMETRY DRAWING FUNCTIONS     *
@@ -330,6 +374,18 @@
     },
 
     /**
+     * Method: drawText
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    drawText: function(node, geometry) {
+        this.drawTextLine(node, geometry);
+    },
+
+    /**
      * Method: drawCircle
      * This method is only called by the renderer itself.
      * 
@@ -355,8 +411,34 @@
         }    
             
     },
-    
+
     /**
+     * Method: drawTextLine
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    drawTextLine: function(node, geometry) {
+        var resolution = this.getResolution();
+        var x = (geometry.x / resolution + this.left);
+        var y = (geometry.y / resolution - this.top);
+        var draw = true;
+        if (x < -this.maxPixel || x > this.maxPixel) { draw = false; }
+        if (y < -this.maxPixel || y > this.maxPixel) { draw = false; }
+
+        if (draw) { 
+            node.setAttributeNS(null, "x", x);
+            node.setAttributeNS(null, "y", -y);
+        } else {
+            this.root.removeChild(node);
+        }    
+            
+    },
+
+
+    /**
      * Method: drawLineString
      * This method is only called by the renderer itself.
      * 
Index: lib/OpenLayers/Layer/WFS.js
===================================================================
--- lib/OpenLayers/Layer/WFS.js	(.../tags/2.5)	(revision 8852)
+++ lib/OpenLayers/Layer/WFS.js	(.../branches/textnode)	(working copy)
@@ -74,6 +74,11 @@
      */
     extractAttributes: false,
 
+     /**Should contain the name of the attribute you wish to display in text 
+      *from the point data you are displaying
+      */
+     textAttrToDisplay: null,
+
     /**
      * Constructor: OpenLayers.Layer.WFS
      *


More information about the Dev mailing list