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:
-
关于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)
- 关于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"
}
})
]
- 关于addUniqueValueRules方法,该方法可以视为是通过Rule和Context的特殊情况,等值判断Filter来实现的一个特例.(其内部具体实现过程和使用方式有所不同,参考openlayers/examples/styles-unique.html示例。,但是总觉没那么多必要专门弄个这么接口,想了解的话可以查看Openlayer/examples/styles-unique.html)
总结: Style提供了方便灵活地使用方式,但是也给用户当使用带来了一定的不便,初学者会被这些复杂的使用结构搞混,以至于不知道如何具体使用。感觉这块提供的方式过于灵活了,不如限制严格的规范。上面只是简单的分析了其中的调用原理,对于相对内部结构有所了解的朋友但愿能起到一定的作用。如果你想了解如何使用,个人感觉最好的熟悉方法还是查看案例,然后按照最标准的使用方法来,上面的内容就当帮助你理解使用规范以防止出错….