OpenLayers源码学习笔记--- OpenLayers中样式相关

          Style样式这块其实在整个Openlayers中不算什么特别大重点,但是作为显示的一个元素,这里相关的内容特比多,经常看到有同事因为用错这块导致的各种问题。所以也就整理了下...

一、相关模块功能类比较多,这里先介绍下各个关键类在这个样式渲染环节中的作用和功能:

 

      Symbolizer  样式,记录样式信息的Object。特点:样式只针对具体的点、线、面等对象(点对象所需的样式和面对像所需的样式可能不同,查看其API,集合用户对象可以知道需要设置怎样的样式属性来控制对象显示)。一般不需要用户构建,而是由Style创建出来,内部在使用的时候通过css或者canvas的画笔控制来显示不同效果。

 

      Style   样式类,定义某种状态的完全样式,点线面等全部包括在内。内含defaultStyle属性记录所设置的样式(属性-值)集合对象Object。提供了createSymbolizer方法来为具体的点、线等对象提供Symbolizer样式。

      关于 style中对rule属性的使用控制…产看下面的设计代码分析内容

 

       StyleMap: 图层样式,定义多种状态下的图层样式,其属性styles中存储由key(‘state’), value(‘Style’) 组成的状态-样式键值对构成。  StyleMap信息被赋予到使用样式显示的Vector图层,默认支持四种样式,下面是OP中StyleMap样式的初始状态

Stylemap.styles = {
 
            "default": new OpenLayers.Style(
 
                OpenLayers.Feature.Vector.style["default"]),
 
            "select": new OpenLayers.Style(
 
                OpenLayers.Feature.Vector.style["select"]),
 
            "temporary": new OpenLayers.Style(
 
                OpenLayers.Feature.Vector.style["temporary"]),
 
            "delete": new OpenLayers.Style(
 
                OpenLayers.Feature.Vector.style["delete"])
 
}

         使用的过程中,vector根据不同状态获取不同样式,然后将使用对应的样式显示feature对象。

 

        OpenLayer.Layer.Vector  矢量图层,是最终的显示承载对象,根据和map,控件的交互,可以获取,设置当前feature的状态,然后从stylemap中读取样式,应用到具体的feature显示上。

 

       OpenLayer.Feature.Vector  矢量对象,要显示的基本对象,记录几何信息geometry等,默认使用的时候会应用图层样式,如果对feature单独设置样式,则不管什么都以设置的样式作为显示依据,而不去管图层样式怎么设置

 

 

       Renderer  使用样式和几何对象属性等来展示Feature要素的最终工具,根据不同浏览器和支持情况选用不同的渲染工具,支持的VML,SVG,Canvas。

 

       Rule规则定义,通过定义规则来指定不同情况下的样式属性值,来决定最终样式采用的显示属性(定义规则..)

 

       Filter: Rule规则定义的具体规则都是由Filter来实现和完成的的,规则的具体定义和执行者……

 

       Context:我们知道Style上的${XXX}取值方法,通过XXX命名的属性可以动态设置属性的值作为样式的备选值(参加下面的示例1)

       Context提供了另外一种更为直接的方法,XXX不指定属性名,而直接指定对应的方法,在方法内部完成由属性值–>样式属性的转换。使用方式类似,相比之下Context更为直接,直接使用方法,接受feature作为参数,内部通过复杂的逻辑处理来获取最终结果,因此提供了更为灵活和多边的样式设置方式。Context可以提供比直接取值更大的操作变化。下面给出两个示例(都是从OpenLayers的examples中直接借过来的说..完成示例可以查看OpenLayers/examples/styles_context.html     OpenLayers/examples/styles_map.html)

     示例一:使用${XXX},xxx代表属性名

//feature添加type属性
             var features = new Array(50);
             for (var i=0; i<features.length; i++) {
                features[i] = new OpenLayers.Feature.Vector(
                    new OpenLayers.Geometry.Point(
                        (360 * Math.random()) - 180, (180 * Math.random()) - 90
                    ), {
                        type: 5 + parseInt(5 * Math.random())
                    }
                );
            }
           //设置${}取值,使用type的值来设置点对象半径
 
          var myStyles = new OpenLayers.StyleMap({
                "default": new OpenLayers.Style({
                    pointRadius: "${type}", // 通过属性名取值,作为样式的值
                    fillColor: "#ffcc66",
                    strokeColor: "#ff9933",
                    strokeWidth: 2,
                    graphicZIndex: 1
                }),
                "select": new OpenLayers.Style({
                    fillColor: "#66ccff",
                    strokeColor: "#3399ff",
                    graphicZIndex: 2
                })
            });

示例二:使用${XXX},XXX代表调用方法

var features = new Array(50);
            for (var i=0; i<features.length; i++) {
                features[i] = new OpenLayers.Feature.Vector(
                    new OpenLayers.Geometry.Point(
                        (360 * Math.random()) - 180, -90 * Math.random()
                    ), {
                        type: 5 + parseInt(5 * Math.random())
                    }
                );
            }
            // 定义context,主要是定义根据属性获取样式值的方法create the layer styleMap by giving the default style a context
            var colors = ["red", "green", "blue"];
            var context = {
                getColor: function(feature) {
                    var region = parseInt((feature.geometry.x + 180) / 120);
                    return colors[region];
                },
                getSize: function(feature) {
                    return feature.attributes["type"] / map.getResolution() * .703125;
                }
            };
           //使用方法,设置样式..
           var template = {
                pointRadius: "${getSize}", // using context.getSize(feature)
                fillColor: "${getColor}" // using context.getColor(feature)
            };
            //生成样式并最终使用样式
            var style = new OpenLayers.Style(template, {context: context});
            var layer2 = new OpenLayers.Layer.Vector('Points', {
                styleMap: new OpenLayers.StyleMap(style),
                renderers: renderer
            });

二、样式的使用。我们所使用的样式中一般有两个样式的最终源头(即用户可以设置修改样式的方法)

         1、  设置layer.vector的style(Object对象)或者styleMap(key: Style对象)样式。style直接使用,styleMap用来根据featur的几何对象和状态来设置对应的样式,这个环节中使用了对规则rule和context的相关使用,后面专门介绍。

         2、  直接设置feature的样式Style。Object类型

        使用优先级比较  feature.style  >  layer.style   > layer.StyleMap

        前两个都是简单的Object对象,意味着用户知道具体的feature类型是点还是线面,知道需要设置哪些样式才有效,然后设置固定样式,所以针对性好,但是不具备状态属性和,也不存在对rule和context使用时候的灵活性。。。。关于这块的区别在下面的分析中会看的更清楚些

三、最后来说明下最标准完备的StyleMap,Style,Rule,Context,Symbolizer,Vector,Renderer的关系。。(相互之间调用和生成的顺序关系),我们假设设置StyleMap:

    1. 关于StyleMap的初始化,这里贴出StyleMap的构造函数源码说明:

      initialize: function (style, options) {
              this.styles = {
                  "default": new OpenLayers.Style(
                      OpenLayers.Feature.Vector.style["default"]),
                  "select": new OpenLayers.Style(
                      OpenLayers.Feature.Vector.style["select"]),
                  "temporary": new OpenLayers.Style(
                      OpenLayers.Feature.Vector.style["temporary"]),
                  "delete": new OpenLayers.Style(
                      OpenLayers.Feature.Vector.style["delete"])
              };
               
              // take whatever the user passed as style parameter and convert it
              // into parts of stylemap.
              if(style instanceof OpenLayers.Style) {
                  // user passed a style object
                  this.styles["default"] = style;
                  this.styles["select"] = style;
                  this.styles["temporary"] = style;
                  this.styles["delete"] = style;
              } else if(typeof style == "object") {
                  for(var key in style) {
                      if(style[key] instanceof OpenLayers.Style) {
                          // user passed a hash of style objects
                          this.styles[key] = style[key];
                      } else if(typeof style[key] == "object") {
                          // user passsed a hash of style hashes
                          this.styles[key] = new OpenLayers.Style(style[key]);
                      } else {
                          // user passed a style hash (i.e. symbolizer)
                          this.styles["default"] = new OpenLayers.Style(style);
                          this.styles["select"] = new OpenLayers.Style(style);
                          this.styles["temporary"] = new OpenLayers.Style(style);
                          this.styles["delete"] = new OpenLayers.Style(style);
                          break;
                      }
                  }
              }
              OpenLayers.Util.extend(this, options);
          },

      代码分析,其设计基本如下:第一步默认读取vector中定义的样式来完成初始化。。第二步通过样式参数来设置具体的用户样式,

      分多种情况对象:

      1)         独立的样式对象Style,将四种状态样式都设置为此,即相当于没有状态区别

      2)         样式组成的键值对key:Style(或者Object),读取提供的样式,覆盖默认值,如果后面的值不是Style对象,内部做封装后设置

      3)         只是一个Object对象,直接封装成Style样式,然后同1)

    2. 关于StyleMap—>style—>Symbolizer的转换过程,中间使用到的Rule,filter,context,等:

      1)       矢量图层使用StyleMap的createSymbolizer: function(feature, intent)方法来获取一个Symbolizer对象。

                参数: feature  具体的矢量元素,包含几何信息,attribute等

                Intent: 记录feature的当前状态,”default”或者”select”….

      2)       查看StyleMap的源码会发现在其CreateSymbolizer中最终是通过调用Style的CreateSymbolizer方法来获取的样式结果的。源码如下:

                this.styles[intent].createSymbolizer(feature)

      3)       Style的CreateSymbolizer方法分解:

               A、关于context的使用:

                 通过方法调用createLiterals ---> createLiteral,最终由OpenLayers.String.format 来完成对属性值标记${property} 到样式值的转换

      OpenLayers.Style.createLiteral = function(value, context, feature, property) {
          if (typeof value == "string" && value.indexOf("${") != -1) {
              value = OpenLayers.String.format(value, context, [feature, property]);
              value = (isNaN(value) || !value) ? value : parseFloat(value);
          }
          return value;
      };
          return value;
      } ;

          B、关于Rule的使用

                    同样在createSymbolizer方法中

                    a)         通过调用rule的evaluate方法来判断是否对当前Feature应用规则,其中rule的evaluate方法调用的是具体Filter中的同名方法实现

                    b)         如果使用规则,则调用applySymbolizer(rule, style, feature)来实现对规则中定义的样式添加到返回样式结果中。

                    c)         多个规则多次应用。

      举例说明:如果针对feature的属性foo来设置,当foo的值小于25的时候我们使用蓝色图片,但其位于25和50之间,使用绿色图片,位于50和75之间使用使用金色图片,其他else的情况下使用默认图片。那么我们则需要根据不同条件定义不同的规则,需要4个rule对象,rules数组的定义如下:(代码来自OpenLayers/examples/style_rule.js),可运行对应的案例查看结果(完成示例参考OpenLayers/examples/style-rules.html)

      rules: [
                      new OpenLayers.Rule({
                          // a rule contains an optional filter
                          filter: new OpenLayers.Filter.Comparison({
                              type: OpenLayers.Filter.Comparison.LESS_THAN,
                              property: "foo", // the "foo" feature attribute
                              value: 25
                          }),
                          // if a feature matches the above filter, use this symbolizer
                          symbolizer: {
                              externalGraphic: "../img/marker-blue.png"
                          }
                      }),
                      new OpenLayers.Rule({
                          filter: new OpenLayers.Filter.Comparison({
                              type: OpenLayers.Filter.Comparison.BETWEEN,
                              property: "foo",
                              lowerBoundary: 25,
                              upperBoundary: 50
                          }),
                          symbolizer: {
                              externalGraphic: "../img/marker-green.png"
                          }
                      }),
                      new OpenLayers.Rule({
                          filter: new OpenLayers.Filter.Comparison({
                              type: OpenLayers.Filter.Comparison.BETWEEN,
                              property: "foo",
                              lowerBoundary: 50,
                              upperBoundary: 75
                          }),
                          symbolizer: {
                              externalGraphic: "../img/marker-gold.png"
                          }
                      }),
                      new OpenLayers.Rule({
                          // apply this rule if no others apply
                          elseFilter: true,
                          symbolizer: {
                              externalGraphic: "../img/marker.png"
                          }
                      })
                  ]
    3.    关于addUniqueValueRules方法,该方法可以视为是通过Rule和Context的特殊情况,等值判断Filter来实现的一个特例.(其内部具体实现过程和使用方式有所不同,参考openlayers/examples/styles-unique.html示例。,但是总觉没那么多必要专门弄个这么接口,想了解的话可以查看Openlayer/examples/styles-unique.html)

      总结: Style提供了方便灵活地使用方式,但是也给用户当使用带来了一定的不便,初学者会被这些复杂的使用结构搞混,以至于不知道如何具体使用。感觉这块提供的方式过于灵活了,不如限制严格的规范。上面只是简单的分析了其中的调用原理,对于相对内部结构有所了解的朋友但愿能起到一定的作用。

      如果你想了解如何使用,个人感觉最好的熟悉方法还是查看案例,然后按照最标准的使用方法来,上面的内容就当帮助你理解使用规范以防止出错….

posted @ 2012-07-26 13:33  行走_  阅读(1383)  评论(0编辑  收藏  举报