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 SuperMap.Style( SuperMap.Feature.Vector.style["default"]), "select": new SuperMap.Style( SuperMap.Feature.Vector.style["select"]), "temporary": new SuperMap.Style( SuperMap.Feature.Vector.style["temporary"]), "delete": new SuperMap.Style( SuperMap.Feature.Vector.style["delete"]) }; // take whatever the user passed as style parameter and convert it // into parts of stylemap. if(style instanceof SuperMap.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 SuperMap.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 SuperMap.Style(style[key]); } else { // user passed a style hash (i.e. symbolizer) this.styles["default"] = new SuperMap.Style(style); this.styles["select"] = new SuperMap.Style(style); this.styles["temporary"] = new SuperMap.Style(style); this.styles["delete"] = new SuperMap.Style(style); break; } } } SuperMap.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} 到樣式值的轉換
SuperMap.Style.createLiteral = function(value, context, feature, property) { if (typeof value == "string" && value.indexOf("${") != -1) { value = SuperMap.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提供了方便靈活地使用方式,但是也給用戶當使用帶來了一定的不便,初學者會被這些復雜的使用結構搞混,以至於不知道如何具體使用。感覺這塊提供的方式過於靈活了,不如限制嚴格的規范。上面只是簡單的分析了其中的調用原理,對於相對內部結構有所了解的朋友但願能起到一定的作用。
如果你想了解如何使用,個人感覺最好的熟悉方法還是查看案例,然后按照最標准的使用方法來,上面的內容就當幫助你理解使用規范以防止出錯….
