[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