openlayers2地图控件扩展:图例控件LegendControl
因项目需要在地图中增加图例,以便专题地图查看或输出。
实现思路,折线和多边形图例直接由样式属性创建,多边形直接设置div的样式;折线通过创建svg,设置polyline的样式;点要素的图例比较复杂,目前实现方式是:
1、根据StyleMap里的Filter,找到不同Filter的要素id,这里的查找要过滤掉没有被渲染的要素;
2、根据要素id从图层的渲染层找到对应的html元素,也就是svg标签;
3、处理找到的svg标签即可,主要是移位;
代码如下:
1 OpenLayers.Control.LegendControl = OpenLayers.Class(OpenLayers.Control, { 2 legendDiv: null, 3 layers: [], 4 colorDiv_w: 30, 5 colorDiv_h: 20, 6 /* 7 * layers --Array 8 * */ 9 initialize: function (layers, options) { 10 OpenLayers.Control.prototype.initialize.apply(this, options); 11 12 this.setLayers(layers); 13 }, 14 setMap: function(map) { 15 var me = this; 16 OpenLayers.Control.prototype.setMap.apply(this, arguments); 17 if(me.map){ 18 me.map.events.register("moveend", me, me.redraw); 19 me.map.events.register("changelayer", me, function(evt){ 20 if(evt.property == "visibility") 21 this.redraw(); 22 }); 23 } 24 }, 25 //{Array or Object} 26 setLayers: function(layers){ 27 var me = this; 28 if(OpenLayers.Util.isArray(layers)){ 29 me.layers = layers; 30 } 31 }, 32 addLayer: function(layer){ 33 this.layers.push(layer); 34 35 this.redraw(); 36 }, 37 redraw: function(){ 38 if(this.div.style.display == "none"){ 39 return; 40 } 41 if(this.legendDiv){ 42 this.div.removeChild(this.legendDiv); 43 this.legendDiv = null; 44 } 45 this.draw(); 46 }, 47 display: function(display) { 48 this.div.style.display = (display) ? "" : "none"; 49 }, 50 draw: function() { 51 OpenLayers.Control.prototype.draw.apply(this); 52 53 // create layout divs 54 this.loadContents(); 55 56 return this.div; 57 }, 58 loadContents: function(){ 59 if(!this.legendDiv){ 60 this.legendDiv = document.createElement("div"); 61 this.legendDiv.id = this.id + "_legendDiv"; 62 OpenLayers.Element.addClass(this.legendDiv, "legendDiv"); 63 this.div.appendChild(this.legendDiv); 64 65 // create span 66 var labelSpan = document.createElement("label"); 67 labelSpan.innerHTML = "Legend"; 68 OpenLayers.Element.addClass(labelSpan, "title"); 69 this.legendDiv.appendChild(labelSpan); 70 var brSpan = document.createElement("br"); 71 this.legendDiv.appendChild(brSpan); 72 73 for(var i = 0; i < this.layers.length;i ++){ 74 var layer = this.layers[i]; 75 if(!layer.getVisibility()){ 76 continue; 77 } 78 var geom = getLayerDefaultGeometry(layer); //获取图层中的几何要素 79 if(!geom) 80 continue; 81 82 //one table corresponds to a layer 83 var labelLyr = document.createElement("label"); 84 labelLyr.innerHTML = layer.name; 85 this.legendDiv.appendChild(labelLyr); 86 var tableDiv = document.createElement("table"); 87 this.legendDiv.appendChild(tableDiv); 88 89 var featArray = layer.features; 90 var unrenderFeatures = layer.unrenderedFeatures; 91 var arr = Object.keys(unrenderFeatures); 92 if(arr.length == featArray.length) 93 continue; 94 95 var styleRules = layer.styleMap.styles["default"].rules; 96 var geomType = "point"; 97 //decide symbolizer panel from geometry type 98 if(geom instanceof OpenLayers.Geometry.Point || 99 geom instanceof OpenLayers.Geometry.MultiPoint) { 100 var allFilters = []; 101 var bElseFilter = 2; 102 for(var key in styleRules) { 103 var filter = styleRules[key].filter; 104 if(filter){ 105 allFilters.push(filter); 106 } 107 else{ //no filter 108 bElseFilter = styleRules[key].elseFilter; 109 } 110 } 111 // 112 if(!bElseFilter){ //no else filter --deafault 113 var trDiv = this.createPointLegend(geom.id, "default"); 114 tableDiv.appendChild(trDiv); 115 } 116 else{ //find filter's geometry id 117 var filterIDs = []; 118 119 var findResults = {}; 120 var findCount = 0; 121 for(var dex = 0; dex < featArray.length; dex ++){ 122 if(findCount == allFilters.length + 1) 123 break; 124 125 var feat = featArray[dex]; 126 if(arr.indexOf(feat.id) >= 0) 127 continue; 128 129 var beInfilter = false; 130 for(var fk = 0; fk < allFilters.length; fk ++){ 131 var bFilter = allFilters[fk].evaluate(feat); 132 if(bFilter){ 133 beInfilter = true; 134 if(!findResults[allFilters[fk].toString()]){ 135 var svgId = feat.geometry.id; 136 filterIDs.push({ 137 id:svgId, 138 label: allFilters[fk].toString() 139 }); 140 findResults[allFilters[fk].toString()] = true; 141 findCount ++; 142 } 143 break; 144 } 145 146 } 147 if(!beInfilter && (!findResults["default"])){ //false 148 var svgId = feat.geometry.id; 149 filterIDs.push({ 150 id:svgId, 151 label: "default" 152 }); 153 findResults["default"] = true; 154 findCount ++; 155 } 156 } 157 for(var fDex = 0; fDex < filterIDs.length; fDex ++){ 158 var trDiv = this.createPointLegend(filterIDs[fDex].id, filterIDs[fDex].label); 159 tableDiv.appendChild(trDiv); 160 } 161 } 162 163 continue; //skip next code 164 } else if(geom instanceof OpenLayers.Geometry.LineString || 165 geom instanceof OpenLayers.Geometry.MultiLineString) { 166 geomType = "line"; 167 168 } else if(geom instanceof OpenLayers.Geometry.LinearRing || 169 geom instanceof OpenLayers.Geometry.Polygon || 170 geom instanceof OpenLayers.Geometry.MultiPolygon) { 171 geomType = "polygon"; 172 } 173 174 for(var key in styleRules) { 175 var filter = styleRules[key].filter; 176 var sybol = styleRules[key].symbolizer; 177 var labelTxt = ""; 178 if(filter) { 179 labelTxt = filter.toString(); 180 } 181 else{ 182 labelTxt = "default"; 183 } 184 185 var trDiv = document.createElement("tr"); 186 tableDiv.appendChild(trDiv); 187 var colorTd = document.createElement("td"); 188 trDiv.appendChild(colorTd); 189 190 var labelTd = document.createElement("td"); 191 trDiv.appendChild(labelTd); 192 193 var itemLabel = document.createElement("label"); 194 itemLabel.style = "margin-left:5px;position:relative;top:3px;"; 195 itemLabel.innerHTML = labelTxt; 196 labelTd.appendChild(itemLabel); 197 198 if(geomType == "line"){ 199 if(sybol.Line){ 200 var colorDiv = this.createLineLegend(sybol.Line); 201 colorTd.appendChild(colorDiv); 202 } 203 } 204 else if(geomType == "polygon"){ 205 if(sybol.Polygon){ 206 var colorDiv = this.createPolygonLegend(sybol.Polygon); 207 colorTd.appendChild(colorDiv); 208 } 209 } 210 } 211 } 212 213 } 214 }, 215 createPolygonLegend: function(polygonSybol){ 216 var colorDiv = document.createElement("div"); 217 if(polygonSybol.fill){ 218 var color = this.parseColor(polygonSybol.fillColor, polygonSybol.fillOpacity); 219 colorDiv.style.background = color; 220 } 221 if(polygonSybol.stroke){ 222 var dashStyle = polygonSybol.strokeDashstyle; 223 var color = this.parseColor(polygonSybol.strokeColor, polygonSybol.strokeOpacity); 224 var width = polygonSybol.strokeWidth + "px"; 225 colorDiv.style.border = width + " " + dashStyle + " " + color; 226 } 227 colorDiv.style.height = this.colorDiv_h + "px"; 228 colorDiv.style.width = this.colorDiv_w + "px"; 230 return colorDiv; 231 }, 232 /* 233 * <svg><polyline points="20,27,34,21" fill="none" stroke="#550000" stroke-opacity="1" stroke-width="3" 234 * stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none"></polyline></svg> 235 * */ 236 createLineLegend: function(lineSybol){ 237 var colorDiv = document.createElement("div"); 238 colorDiv.style.height = this.colorDiv_h + "px"; 239 colorDiv.style.width = this.colorDiv_w + "px"; 240 241 var lineHtml = '<svg width="100%" height="100%">'; 242 lineHtml += '<polyline points="2,2,28,18" fill="none" stroke="' + lineSybol.strokeColor 243 + '" stroke-opacity="'+ lineSybol.strokeOpacity +'" stroke-width="'+ lineSybol.strokeWidth + '"'; 244 lineHtml += ' stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none"></polyline></svg>'; 245 colorDiv.innerHTML = lineHtml; 246 247 return colorDiv; 248 }, 249 createPointLegend: function(svgId, label){ 250 var trDiv = document.createElement("tr"); 251 var svgEle = document.getElementById(svgId); 252 if(!svgEle){ 253 return trDiv; 254 } 255 var colorTd = document.createElement("td"); 256 trDiv.appendChild(colorTd); 257 258 var labelTd = document.createElement("td"); 259 trDiv.appendChild(labelTd); 260 var itemLabel = document.createElement("label"); 261 itemLabel.style = "margin-left:5px;position:relative;top:3px;"; 262 itemLabel.innerHTML = label; 263 labelTd.appendChild(itemLabel); 264 265 var colorDiv = document.createElement("div"); 266 colorTd.appendChild(colorDiv); 267 var divHeight = this.colorDiv_h; 268 var divWidth = this.colorDiv_w; 269 270 var cln = svgEle.cloneNode(true); 271 if(cln.nodeName.toLowerCase() != "svg"){ //circle,image 272 var rVal = cln.getAttribute("r"); 273 if(cln.hasAttribute("x") && cln.hasAttribute("y")){ 274 cln.setAttribute("x", rVal); 275 cln.setAttribute("y", rVal); 276 } 277 if(cln.hasAttribute("cx") && cln.hasAttribute("cy")){ 278 cln.setAttribute("cx", rVal); 279 cln.setAttribute("cy", rVal); 280 } 281 if(cln.hasAttribute("transform")){ 282 var transform = cln.getAttribute("transform"); 283 if(transform.indexOf('rotate(') >= 0){ 284 var transValues = transform.split('rotate('); 285 var kk = null; 286 if(transValues.length == 1){ 287 kk = 0; 288 } 289 else if(transValues.length > 1){ 290 kk = 1; 291 } 292 if(kk != null){ 293 var str = transValues[kk]; 294 var sp = str.indexOf(')'); 295 var rotString = str.substring(0, sp); 296 297 var ww = parseFloat(cln.getAttribute('width')); 298 var hh = parseFloat(cln.getAttribute('height')); 299 if(ww >= divWidth) 300 divWidth = ww + 2; 301 if(hh >= divWidth) 302 divHeight = hh + 2; 303 304 var rotValues = rotString.split(' '); 305 rotValues[1] = ww / 2; 306 rotValues[2] = hh / 2; 307 308 transValues[kk] = rotValues.join(' ') + str.substring(sp); 309 cln.setAttribute('transform',transValues.join('rotate(')); 310 } 311 } 312 313 } 314 //innerHTML 315 colorDiv.innerHTML = '<svg width="100%" height="100%"></svg>'; 316 var svgNode = colorDiv.firstChild; 317 svgNode.appendChild(cln); 318 colorDiv.appendChild(svgNode); 319 } 320 else{ 321 //change viewBox --from(0,0) 322 var viewBox = cln.getAttribute('viewBox'); // Grab the object representing the SVG element's viewBox attribute. 323 var viewBoxValues = viewBox.split(' '); // Create an array and insert each individual view box attribute value (assume they're seperated by a single whitespace character). 324 325 /* The array is filled with strings, convert the first two viewBox values to floats: */ 326 viewBoxValues[0] = parseFloat(viewBoxValues[0]); // Represent the x-coordinate on the viewBox attribute. 327 viewBoxValues[1] = parseFloat(viewBoxValues[1]); // Represent the y coordinate on the viewBox attribute. 328 viewBoxValues[2] = parseFloat(viewBoxValues[2]); // Represent the y coordinate on the viewBox attribute. 329 330 if(viewBoxValues[2] > 300){ //star 331 viewBoxValues[0] = 250; 332 viewBoxValues[1] = 75; 333 } 334 else{ 335 viewBoxValues[0] = 0; 336 viewBoxValues[1] = 0; 337 } 338 339 cln.setAttribute('viewBox', viewBoxValues.join(' ')); 340 colorDiv.appendChild(cln); 341 } 342 343 colorDiv.style.height = divHeight + "px"; 344 colorDiv.style.width = divWidth + "px"; 345 return trDiv; 346 }, 347 parseColor: function(value, opacity){ 348 if(value.length == 7){ 349 var str = value.substr(1,6); 350 var rgb1 = parseInt(str.substr(0, 2), 16); 351 var rgb2 = parseInt(str.substr(2, 2), 16); 352 var rgb3 = parseInt(str.substr(4, 2), 16); 353 return "rgba("+rgb1+","+rgb2+","+rgb3+","+opacity+")"; 354 } 355 else{ 356 return value; 357 } 358 }, 359 CLASS_NAME: "OpenLayers.Control.LegendControl" 360 });
LegendControl的样式设置如下:
1 /* 2 * olControlLegendControl 3 */ 4 .olControlLegendControl { 5 position: absolute; 6 left: 0px; 7 bottom: 0px; 8 width: 18em; 9 font-family: sans-serif; 10 font-size: smaller; 11 margin-top: 3px; 12 margin-left: 0px; 13 margin-bottom: 0px; 14 color: darkblue; 15 background-color: transparent; 16 } 17 .olControlLegendControl .legendDiv { 18 padding-top: 5px; 19 padding-left: 10px; 20 padding-bottom: 5px; 21 padding-right: 10px; 22 background-color: rgba(200, 200, 200, 0.5); 23 } 24 .olControlLegendControl .legendDiv .title{ 25 margin-top: 3px; 26 margin-left: 40px; 27 margin-bottom: 3px; 28 display: inline-block; 29 height: 30px; 30 font-family: sans-serif; 31 font-weight: bold; 32 font-size: 20px; 33 } 34 .olControlLegendControl .legendDiv .item{ 35 margin-top: 3px; 36 margin-left: 3px; 37 margin-bottom: 3px; 38 }
效果图