使用openlayers扩展插件ol-ext设置地图指定区域高亮

最近要实现一个从底图向上发光的功能,着实纠结了好久,起初像是使用polygon 颜色透明度来实现,但毕竟底图不亮,增加图层效果不理想呀

一、ui设计是这样

二、绘制面

为底图增加一个MultiPolygon来实现效果,

代码片段

    import {styleSwitch} from '@/components/common/set_style';
        
	mapInit(){
       this.mapObj = new Map({
              target: el,
              view: view
          });
     // 初始geoJson
     var vectorSource = new VectorSource({
         features: (new GeoJSON()).readFeatures(self.geojsonObject)
     });

     var vectorLayer = new VectorLayer({
         renderMode: "image",
         source:vectorSource,
         style:self.styleFunction,
         maxResolution: 2,
         zIndex:2
     });
      this.mapObj.addLayer(vectorLayer);
    },
    styleFunction(feature, resolution) {

        return styleSwitch(feature.getGeometry().getType(), resolution, feature)
    },
     
set_style.js
	import {Fill, Stroke, Style, RegularShape, Text, Icon,Circle,} from 'ol/style';
	import { DEVICE_PIXEL_RATIO } from "ol/has";
	/**
	 * 主要景区 polygon ,multipolygon, line , multiline 样式设置
	 * 根据geojson类型设置地图样式
	 * @param type
	 * @param resolution
	 * @param feature
	 * @return {string}
	 */
	export function styleSwitch(type, resolution, feature) {
	    let canvas = document.createElement("canvas");
	    let context = canvas.getContext("2d");
	    // Generate a rainbow gradient
	    let gradient = (() => {
	        let grad = context.createLinearGradient(
	            220*DEVICE_PIXEL_RATIO,20*DEVICE_PIXEL_RATIO,4220*DEVICE_PIXEL_RATIO,220*DEVICE_PIXEL_RATIO
	        );
	        // 设置开始结束颜色
	        grad.addColorStop(0, "rgba(0,192,50,0.4)");
	        grad.addColorStop(0.5, "rgba(0,192,50,0.2)");
	        grad.addColorStop(1, "rgba(1,134,82,0.5)");
	
	        return grad;
	    })();
	
	    var text = resolution < 0.0054931640625 ? feature.get('name') : '';    //根据分辨率控制文字显示级别
	    let styleItem = [];
	    switch (type) {
	        case 'Point':
	        case 'MultiPoint':
	            styleItem = [
	                new Style({
	                    image: new Icon({
	                        src: 'images/left_module/police/police_topSmallIcon_second.png',
	                        size: [24, 26],
	                        color: '#ff0000'
	                    }),
	                    text: new Text({
	                        offsetX: 14,
	                        offsetY: 10,
	                        font: '12px Calibri,sans-serif',
	                        text: text,
	                        fill: new Fill({
	                            color: '#DC143C'
	                        }),
	                        stroke: new Stroke({
	                            color: '#fff',
	                            width: 3
	                        })
	                    })
	                })
	            ]
	            break;
	        case 'MultiLineString':
	        case 'LineString':
	            styleItem = [
	                new Style({
	                    stroke: new Stroke({
	                        color: 'rgba(247,246,46, 0.2)',
	                        lineDash: [5],
	                        width: 8
	                    })
	                }),
	                new Style({
	                    stroke: new Stroke({
	                        color: "rgba(247,246,46, 1)",
	                        lineDash: [5],
	                        width: 3
	                    }),
	                })
	            ]
	            break;
	        case  'Polygon':
	        case 'MultiPolygon':
	
	            styleItem = [
	                new Style({
	                    // fill: new Fill({
	                    //     color: gradient
	                    // }),
	                    stroke: new Stroke({
	                        color: "rgba(255,255,255,0.1)",
	                        width: 2
	                    }),
	                    zIndex: 0
	                }),
	                new Style({
	                    stroke: new Stroke({
	                        color: 'rgba(245,255,250, 0.2)',
	                        lineCap: 'round',
	                        lineJoin:'bevel',
	                        width: 10
	                    })
	                }),
	                new Style({
	                    stroke: new Stroke({
	                        color: "rgba(0,149,32,0.1)",
	                        lineCap: 'round',
	                        lineJoin:'bevel',
	                        width: 2
	                    })
	                }),
	
	                // new Style({
	                //     fill: new Fill({
	                //         color: [0,149,32, 0.8]
	                //     })
	                // }),
	            ]
	            break;
	        case  'GeometryCollection':
	            styleItem = [
	                new Style({
	                    stroke: new Stroke({
	                        color: 'magenta',
	                        width: 2
	                    }),
	                    fill: new Fill({
	                        color: 'magenta'
	                    }),
	                    image: new Icon({
	                        src: 'images/left_module/police/police_topSmallIcon_second.png',
	                        size: [24, 26],
	                        color: '#ff0000'
	                    }),
	                    text: new Text({
	                        font: '12px Calibri,sans-serif',
	                        text: text,
	                        fill: new Fill({
	                            color: '#DC143C'
	                        }),
	                        stroke: new Stroke({
	                            color: '#fff',
	                            width: 3
	                        })
	                    })
	                })
	            ]
	            break;
	        case 'Circle':
	            styleItem = [
	                new Style({
	                    stroke: new Stroke({
	                        color: 'red',
	                        width: 2
	                    }),
	                    fill: new Fill({
	                        color: 'rgba(255,0,0,0.2)'
	                    }),
	                    image: new Icon({
	                        src: 'images/left_module/police/police_topSmallIcon_second.png',
	                        size: [24, 26],
	                        color: '#ff0000'
	                    }),
	                    text: new Text({
	                        font: '12px Calibri,sans-serif',
	                        text: text,
	                        fill: new Fill({
	                            color: '#DC143C'
	                        }),
	                        stroke: new Stroke({
	                            color: '#fff',
	                            width: 3
	                        })
	                    })
	                })];
	            break;
	    }
	
	    return styleItem;
	}
效果如下

三、使用.Render3D

后来想是否少了3D效果 ? 这又引用了ol-ext.layer.Render3D

效果如下

四、使用.Colorize

看还是相距胜远;于是想到了为底图增加透明色,于是引入了ol-ext.filter.Colorize,

效果如下


颜色是着上了,可是 看源码得知


	ol_filter_Colorize.prototype.postcompose = function(e) {
		// Set back color hue
		var ctx = e.context;
		var canvas = ctx.canvas;
	
		
		ctx.save();
			if (this.get('operation')=='enhance')
			{	var v = this.get('value');
				if (v)
				{	var w = canvas.width;
					var h = canvas.height;
					ctx.globalCompositeOperation = 'color-burn'
					ctx.globalAlpha = v;
					ctx.drawImage (canvas, 0, 0, w, h);
					ctx.drawImage (canvas, 0, 0, w, h);
					ctx.drawImage (canvas, 0, 0, w, h);
				}
			}
			else
			{	ctx.globalCompositeOperation = this.get('operation');
				ctx.fillStyle = this.get('color');
				ctx.fillRect(0,0,canvas.width,canvas.height);  
			}
		ctx.restore();
	}

五、 使用.Mask 加 .Crop

这直接是使用canvas绘制的一个面,很明显是方的了,最后想来想去想到使用ol-ext.filter.Colorize来为底图着色,再使用ol-ext.filter.Maskol-ext.filter.Crop来根据坐标绘制凸显的面同时为地图增加蒙层,但是又遇到了ol-ext.filter.Mask绘制的面和layerVector位置不合问题

预览效果是这样的
canvas绘制和layerVector绘制的面偏移较多
但地图缩放时更明显
使用openlayers扩展插件ol-ext设置地图指定区域高亮_第1张图片但地图缩放时更明显
代码片段

	<script type="text/ecmascript-6">
	    import 'ol/ol.css';
	    import Map from 'ol/Map';
	    import View from 'ol/View';
	    import {XYZ, Vector as VectorSource} from 'ol/source';
	    import {Fill} from 'ol/style';
	    import Point from 'ol/geom/Point';
	    import Feature from 'ol/Feature';
	    import GeoJSON from 'ol/format/GeoJSON';
	    import olExtColorize from 'ol-ext/filter/Colorize'
	    import olExtCrop from 'ol-ext/filter/Crop'
	    import olExtMask from 'ol-ext/filter/Mask'
	    import MultiPolygon from 'ol/geom/MultiPolygon';
	    
		export default = {
			methods:{
			   mapInit(polygonCoordiantes) {
			        let self = this;
	                let proj = 'EPSG:4326';
	                let el = this.$refs.map;
	                let padLeft = (val, num, radix) => {
	                    let str = val.toString(radix || 10);
	                    return (new Array(num).join('0') + str).slice(-num);
	                }
	                var view = new View({
	                    projection: proj,
	                    center: [103.37324413479338, 29.544684360197113],
	                    minZoom: 10,
	                    zoom: 13,
	                    maxZoom: 15,
	                    extent: [102.1000671387,28.7086486816,104.7244262695,30.0448608398],
	                });

				     this.mapObj = new Map({
		                   target: el,
		                   view: view
		             });
		             
		            var leshan_tile = this.initLeshanTile(proj, padLeft);
		
		            this.mapObj.addLayer(leshan_tile);
		       		 // 點亮地圖
		            this.lingUpTheMap(leshan_tile);
		
		            this.drawPolygonAndAddMask(leshan_tile,polygonCoordiantes)
			},
		   /**
		    *
		    * 乐山瓦片加载
		    */
		   initLeshanTile(proj, padLeft) {
		       // 乐山 瓦片图层
		       let layers_leshan = new TileLayer({
		           source: new XYZ({
		               crossOrigin: "anonymous",
		               projection: proj,
		               url: 'http://localhost:808/image_map/_alllayers/',
		
		               tileUrlFunction: function (tileCoord, pixelRatio, proj) {
		                   var x = 'C' + padLeft(tileCoord[1], 8, 16);
		                   var y = 'R' + padLeft(tileCoord[2] -1, 8, 16);
		                   var z = 'L' + padLeft(tileCoord[0], 2, 10);
		                   var Newurl = 'http://localhost:808/image_map/_alllayers/' + z + '/' + y + '/' + x + '.png';
		                   return Newurl;
		               }
		           }),
		           visible: true
		       });
		
		       return layers_leshan;
		   },
			  /**
		       *
		       * 點亮地圖
		       */
		      lingUpTheMap(osm,){
		          // Enhance filter
		          var enhance = new olExtColorize({ operation:'enhance'});
		          osm.addFilter(enhance);
		
		          // Custom filter
		          var filter = new olExtColorize();
		          osm.addFilter(filter);
		
		          enhance.setActive(false);
		          filter.setActive(true);
		          filter.setFilter({
		              operation:'color',
		              red:Number('0'),
		              green: Number('192'), blue: Number('50'),
		              value: Number('1'),
		          });
		      },
			  /**
		       * 绘制面,同时增加蒙层
		       * @param osm {Object} tile 对象
		       * @param coordinatesOfPolygon {Array[[]]} 面的坐标数据
		       */
		      drawPolygonAndAddMask(osm,coordinatesOfPolygon){
		
		          var f = new Feature(new MultiPolygon(coordinatesOfPolygon));
		          var crop = new olExtCrop({
		              feature: f,
		              inner: false
		          });
		          osm.addFilter(crop);
		          var mask = new olExtMask({
		              feature: f,
		              inner: false,
		              fill: new Fill({
		                  color: [255, 255, 255, 0.8]
		              })
		          });
		          osm.addFilter(mask);
		
		          mask.set('inner',false);
		          crop.set('inner', false);
		          mask.fillColor_ = 'rgba(0,0,0,0.8)';
		          // Activate
		          mask.set('active', true);
		          crop.set('active', false);
		      },
			}
			
		}
	</script>

六、处理canvas 绘制偏移问题

如上虽然实现了底图着色但是绘制的元素偏移这么多,这显然不是我想要的结果

后来测试了不知道多少遍,查了不知道多少资料没有一个是我要的答案!

最后没办法再次查看ol-ext.filter.Mask.js源码

一遍又一遍看其中重要 的drawFeaturePath_ 属性方法


	/** Draw the feature into canvas */
	ol_filter_Mask.prototype.drawFeaturePath_ = function(e, out)
	{	var ctx = e.context;
		var canvas = ctx.canvas;
		var ratio = e.frameState.pixelRatio;
		// Transform
		var m = e.frameState.coordinateToPixelTransform;
		var tr = function(pt)
		{	return [
				(pt[0]*m[0]+pt[1]*m[1]+m[4])*ratio,
				(pt[0]*m[2]+pt[1]*m[3]+m[5])*ratio
			];
		}
		// Old ol version
		if (!m)
		{	m = e.frameState.coordinateToPixelMatrix;
			tr = function(pt)
			{	return [
					(pt[0]*m[0]+pt[1]*m[1]+m[12])*ratio,
					(pt[0]*m[4]+pt[1]*m[5]+m[13])*ratio
				];
			}
		}
		// Geometry
		var ll = this.feature_.getGeometry().getCoordinates();
		if (this.feature_.getGeometry().getType()=="Polygon") ll = [ll];
		ctx.beginPath();
	        if (out)
			{	ctx.moveTo (0,0);
				ctx.lineTo (canvas.width, 0);
				ctx.lineTo (canvas.width, canvas.height);
				ctx.lineTo (0, canvas.height);
				ctx.lineTo (0, 0);
			}
			for (var l=0; l<ll.length; l++)
			{	var c = ll[l];
				for (var i=0; i<c.length; i++) 
				{	var pt = tr(c[i][0]);
					ctx.moveTo (pt[0], pt[1]);
					for (var j=1; j<c[i].length; j++) 
					{	pt = tr(c[i][j]);
						ctx.lineTo (pt[0], pt[1]);
					}
				}
			}
	}

如下这句代码引起了我的注意


	var ratio = e.frameState.pixelRatio;
	

查看API
使用openlayers扩展插件ol-ext设置地图指定区域高亮_第2张图片
大概意思是 帧的像素比率

这个方法明细是使用canvas 根据当前feature的坐标结合当前像素 帧 来绘制元素的, 然 帧的像素比率 会根据地图缩放而发生改变,所以绘制的面元素也随着像素变法不停发生偏移,最后想得到不根据帧的像素比率 来绘制元素他的位置不就对了吗!最后去掉 ratio;修噶代码为


	// Transform
	var m = e.frameState.coordinateToPixelTransform;
	var tr = function(pt)
	{	return [
			(pt[0]*m[0]+pt[1]*m[1]+m[4]),
			(pt[0]*m[2]+pt[1]*m[3]+m[5])
		];
	}
	
再运行看效果


这次效果终于要好点了,但是还有待改进,毕竟离ui设计图还有一些距离,加油继续…

你可能感兴趣的:(vue,canvas,webgl)