[OpenLayers-Commits] r7324 - in trunk/openlayers: examples lib/OpenLayers/Format/SLD lib/OpenLayers/Renderer tests/Renderer

commits at openlayers.org commits at openlayers.org
Fri Jun 6 14:42:44 EDT 2008


Author: ahocevar
Date: 2008-06-06 14:42:44 -0400 (Fri, 06 Jun 2008)
New Revision: 7324

Added:
   trunk/openlayers/examples/styles-rotation.html
Modified:
   trunk/openlayers/lib/OpenLayers/Format/SLD/v1.js
   trunk/openlayers/lib/OpenLayers/Renderer/SVG.js
   trunk/openlayers/lib/OpenLayers/Renderer/VML.js
   trunk/openlayers/tests/Renderer/VML.html
Log:
Implemented rotation of externalGraphic vector point features. r=tschaub (closes #1433)

Added: trunk/openlayers/examples/styles-rotation.html
===================================================================
--- trunk/openlayers/examples/styles-rotation.html	                        (rev 0)
+++ trunk/openlayers/examples/styles-rotation.html	2008-06-06 18:42:44 UTC (rev 7324)
@@ -0,0 +1,83 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>OpenLayers Styles Rotation Example</title>
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 475px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        
+        var map;
+        var vectors;
+        
+        function init(){
+            map = new OpenLayers.Map('map');
+            var wms = new OpenLayers.Layer.WMS(
+                "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0",
+                {layers: 'basic'}
+            );
+
+            vectors = new OpenLayers.Layer.Vector(
+                "Simple Geometry",
+                {
+                    styleMap: new OpenLayers.StyleMap({
+                        "default": {
+                            externalGraphic: "../img/marker-gold.png",
+                            //graphicWidth: 17,
+                            graphicHeight: 20,
+                            graphicYOffset: -19,
+                            rotation: "${angle}",
+                            fillOpacity: "${opacity}"
+                        },
+                        "select": {
+                            cursor: "crosshair",
+                            externalGraphic: "../img/marker.png"
+                        }
+                    })
+                }
+            );
+            
+            map.addLayers([wms, vectors]);
+            
+            var features = [];
+            var x = -111.04;
+            var y = 45.68;
+            for(var i = 0; i < 10; i++){
+                x += i * .5;
+                y += i * .1;
+                features.push(
+                    new OpenLayers.Feature.Vector(
+                        new OpenLayers.Geometry.Point(x, y), {angle: (i*36)%360-180, opacity:i/10+.1}
+                    )
+                );
+                features.push(
+                    new OpenLayers.Feature.Vector(
+                        new OpenLayers.Geometry.Point(x, y), {angle: (i*36)%360, opacity:i/10+.1}
+                    )
+                );
+            }
+            
+            map.setCenter(new OpenLayers.LonLat(x-10, y), 5);
+            vectors.addFeatures(features);
+
+            var selectControl = new OpenLayers.Control.SelectFeature(
+                vectors, {hover: true});
+            map.addControl(selectControl);
+            selectControl.activate();
+
+        };
+        
+    </script>
+  </head>
+  <body onload="init()">
+    <h1 id="title">Rotation Styles Example</h1>
+    <p id="shortdesc">Vector point feature symbolizers can have a <tt>rotation</tt> property. The center of the rotation is the point of the image specified by <tt>graphicXOffset</tt> and <tt>graphicYOffset</tt>.</p>
+    <div id="map"></div>
+    <div id="docs"/>
+  </body>
+</html>


Property changes on: trunk/openlayers/examples/styles-rotation.html
___________________________________________________________________
Name: svn:keywords
   + Id Author Date Revision
Name: svn:eol-style
   + native

Modified: trunk/openlayers/lib/OpenLayers/Format/SLD/v1.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Format/SLD/v1.js	2008-06-06 16:37:34 UTC (rev 7323)
+++ trunk/openlayers/lib/OpenLayers/Format/SLD/v1.js	2008-06-06 18:42:44 UTC (rev 7324)
@@ -227,6 +227,9 @@
                 if(graphic.href != undefined) {
                     symbolizer.externalGraphic = graphic.href;
                 }
+                if(graphic.rotation != undefined) {
+                    symbolizer.rotation = graphic.rotation;
+                }
             },
             "ExternalGraphic": function(node, graphic) {
                 this.readChildNodes(node, graphic);

Modified: trunk/openlayers/lib/OpenLayers/Renderer/SVG.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Renderer/SVG.js	2008-06-06 16:37:34 UTC (rev 7323)
+++ trunk/openlayers/lib/OpenLayers/Renderer/SVG.js	2008-06-06 18:42:44 UTC (rev 7324)
@@ -222,6 +222,12 @@
             } else {
                 node.setAttributeNS(null, "r", style.pointRadius);
             }
+
+            if (style.rotation) {
+                var rotation = OpenLayers.String.format(
+                    "rotate(${0} ${1} ${2})", [style.rotation, x, y]);
+                node.setAttributeNS(null, "transform", rotation);
+            }
         }
         
         if (options.isFilled) {

Modified: trunk/openlayers/lib/OpenLayers/Renderer/VML.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Renderer/VML.js	2008-06-06 16:37:34 UTC (rev 7323)
+++ trunk/openlayers/lib/OpenLayers/Renderer/VML.js	2008-06-06 18:42:44 UTC (rev 7324)
@@ -171,6 +171,7 @@
                 node.style.top = ((geometry.y/resolution)-(yOffset+height)).toFixed();
                 node.style.width = width;
                 node.style.height = height;    
+                node.style.flip = "y";
                 
                 // modify style/options for fill and stroke styling below
                 style.fillColor = "none";
@@ -209,11 +210,20 @@
                 
                 fill.setAttribute("src", style.externalGraphic);
                 fill.setAttribute("type", "frame");
-                node.style.flip = "y";
                 
                 if (!(style.graphicWidth && style.graphicHeight)) {
                   fill.aspect = "atmost";
                 }                
+                         
+                // additional rendering for rotated graphics
+                if (style.rotation) {
+                    this.graphicRotate(node, xOffset, yOffset);
+                    // make the fill fully transparent, because we now have
+                    // the graphic as imagedata element. We cannot just remove
+                    // the fill, because this is part of the hack described
+                    // in graphicRotate
+                    fill.setAttribute("opacity", 0);
+                }
             }
             if (fill.parentNode != node) {
                 node.appendChild(fill);
@@ -250,6 +260,112 @@
     },
 
     /**
+     * Method: graphicRotate
+     * If a point is to be styled with externalGraphic and rotation, VML fills
+     * cannot be used to display the graphic, because rotation of graphic
+     * fills is not supported by the VML implementation of Internet Explorer.
+     * This method creates a olv:imagedata element inside the VML node,
+     * DXImageTransform.Matrix and BasicImage filters for rotation and
+     * opacity, and a 3-step hack to remove rendering artefacts from the
+     * graphic and preserve the ability of graphics to trigger events.
+     * Finally, OpenLayers methods are used to determine the correct
+     * insertion point of the rotated image, because DXImageTransform.Matrix
+     * does the rotation without the ability to specify a rotation center
+     * point.
+     * 
+     * Parameters:
+     * node    - {DOMElement}
+     * xOffset - {Number} rotation center relative to image, x coordinate
+     * yOffset - {Number} rotation center relative to image, y coordinate
+     */
+    graphicRotate: function(node, xOffset, yOffset) {
+        var style = style || node._style;
+        var options = node._options;
+        
+        var aspectRatio, size;
+        if (!(style.graphicWidth && style.graphicHeight)) {
+            // load the image to determine its size
+            var img = new Image();
+            img.onreadystatechange = OpenLayers.Function.bind(function() {
+                if(img.readyState == "complete" ||
+                        img.readyState == "interactive") {
+                    aspectRatio = img.width / img.height;
+                    size = Math.max(style.pointRadius * 2, 
+                        style.graphicWidth || 0,
+                        style.graphicHeight || 0);
+                    xOffset = xOffset * aspectRatio;
+                    style.graphicWidth = size * aspectRatio;
+                    style.graphicHeight = size;
+                    this.graphicRotate(node, xOffset, yOffset)
+                }
+            }, this);
+            img.src = style.externalGraphic;
+            
+            // will be called again by the onreadystate handler
+            return;
+        } else {
+            size = Math.max(style.graphicWidth, style.graphicHeight);
+            aspectRatio = style.graphicWidth / style.graphicHeight;
+        }
+        
+        var width = Math.round(style.graphicWidth || size * aspectRatio);
+        var height = Math.round(style.graphicHeight || size);
+        node.style.width = width;
+        node.style.height = height;
+        
+        // Three steps are required to remove artefacts for images with
+        // transparent backgrounds (resulting from using DXImageTransform
+        // filters on svg objects), while preserving awareness for browser
+        // events on images:
+        // - Use the fill as usual (like for unrotated images) to handle
+        //   events
+        // - specify an imagedata element with the same src as the fill
+        // - style the imagedata element with an AlphaImageLoader filter
+        //   with empty src
+        var image = document.getElementById(node.id + "_image");
+        if (!image) {
+            image = this.createNode("olv:imagedata", node.id + "_image");
+            node.appendChild(image);
+        }
+        image.style.width = width;
+        image.style.height = height;
+        image.src = style.externalGraphic;
+        image.style.filter =
+            "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + 
+            "src='', sizingMethod='scale')";
+
+        var rotation = style.rotation * Math.PI / 180;
+        var sintheta = Math.sin(rotation);
+        var costheta = Math.cos(rotation);
+
+        // do the rotation on the image
+        var filter =
+            "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
+            ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
+            ",SizingMethod='auto expand')\n"
+
+        // set the opacity (needed for the imagedata)
+        var opacity = style.graphicOpacity || style.fillOpacity;
+        if (opacity && opacity != 1) {
+            filter += 
+                "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + 
+                opacity+")\n";
+        }
+        node.style.filter = filter;
+
+        // do the rotation again on a box, so we know the insertion point
+        var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
+        var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
+        imgBox.rotate(style.rotation, centerPoint);
+        var imgBounds = imgBox.getBounds();
+
+        node.style.left = Math.round(
+            parseInt(node.style.left) + imgBounds.left);
+        node.style.top = Math.round(
+            parseInt(node.style.top) - imgBounds.bottom);
+    },
+
+    /**
      * Method: postDraw
      * Some versions of Internet Explorer seem to be unable to set fillcolor
      * and strokecolor to "none" correctly before the fill node is appended to

Modified: trunk/openlayers/tests/Renderer/VML.html
===================================================================
--- trunk/openlayers/tests/Renderer/VML.html	2008-06-06 16:37:34 UTC (rev 7323)
+++ trunk/openlayers/tests/Renderer/VML.html	2008-06-06 18:42:44 UTC (rev 7324)
@@ -143,7 +143,48 @@
         t.eq(node.style.width, (2 * radius) + "px", "width is correct");
         t.eq(node.style.height, (2 * radius) + "px", "height is correct");
     }
+    
+    function test_VML_drawGraphic(t) {
+        if (!OpenLayers.Renderer.VML.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(6);
+        
+        var r = new OpenLayers.Renderer.VML(document.body);
+        r.resolution = 1;
+        
+        var node = document.createElement('div');
+        node.id = "test"
+        node._geometryClass = "OpenLayers.Geometry.Point";
+        
+        var geometry = {
+            x: 1,
+            y: 2
+        }
+        
+        var style = {
+            externalGraphic: "foo.png",
+            graphicWidth: 7,
+            graphicHeight: 10
+        }
+        
+        r.drawGeometryNode(node, geometry, style);
 
+        t.eq(node.childNodes[0].id, "test_fill", "fill child node correctly created");
+        t.eq(node.style.left, "-3px", "x of insertion point with calculated xOffset correct");
+        t.eq(node.style.top, "-3px", "y of insertion point with calculated yOffset correct");
+        
+        style.rotation = 90;
+        
+        r.drawGeometryNode(node, geometry, style);
+        
+        t.eq(node.childNodes[1].id, "test_image", "image child node correctly created");
+        t.eq(node.style.left, "-4px", "x of insertion point of rotated image correct");
+        t.eq(node.style.top, "-4px", "y of insertion point of rotated image correct");
+    }
+
     function test_VML_drawlinestring(t) {
         if (!OpenLayers.Renderer.VML.prototype.supported()) {
             t.plan(0);



More information about the Commits mailing list