如下图,在手机里面有一个这样的功能,我称之为“相册地图”,本文讲述的是通过扩展ol.style的类,来实现“相册地图”这个功能。
要实现这个功能有两个关键点:1、地图聚合;2、图片样式。有关地图聚类的在很早之前的文章里面已经涉及到过,所以本文重点讲述图片样式。
/** * @classdesc * Set Photo style for vector features. * * @constructor * @param {} options * @param { default | square | round | anchored | folio } options.kind * @param {boolean} options.crop crop within square, default is false * @param {Number} options.radius symbol size * @param {boolean} options.shadow drop a shadow * @param {ol.style.Stroke} options.stroke * @param {String} options.src image src * @param {String} options.crossOrigin The crossOrigin attribute for loaded images. Note that you must provide a crossOrigin value if you want to access pixel data with the Canvas renderer. * @param {Number} options.offsetX Horizontal offset in pixels. Default is 0. * @param {Number} options.offsetY Vertical offset in pixels. Default is 0. * @param {function} options.onload callback when image is loaded (to redraw the layer) * @extends {ol.style.RegularShape} * @implements {ol.structs.IHasChecksum} * @api */ ol.style.Photo = function(options) { options = options || {}; this.sanchor_ = options.kind=="anchored" ? 8:0; this.shadow_ = Number(options.shadow) || 0; if (!options.stroke) { options.stroke = new ol.style.Stroke({ width: 0, color: "#000"}) } var strokeWidth = options.stroke.getWidth(); if (strokeWidth<0) strokeWidth = 0; if (options.kind=='folio') strokeWidth += 6; options.stroke.setWidth(strokeWidth); ol.style.RegularShape.call (this, { radius: options.radius + strokeWidth + this.sanchor_/2 + this.shadow_/2, points:0 // fill:new ol.style.Fill({color:"red"}) // No fill to create a hit detection Image }); // Hack to get the hit detection Image (no API exported) if (!this.hitDetectionCanvas_) { var img = this.getImage(); for (var i in this) { if (this[i] && this[i].getContext && this[i]!==img) { this.hitDetectionCanvas_ = this[i]; break; } } } this.stroke_ = options.stroke; this.fill_ = options.fill; this.crop_ = options.crop; this.crossOrigin_ = options.crossOrigin; this.kind_ = options.kind || "default"; this.radius_ = options.radius; this.src_ = options.src; this.offset_ = [options.offsetX ? options.offsetX :0, options.offsetY ? options.offsetY :0]; this.onload_ = options.onload; if (typeof(options.opacity)=='number') this.setOpacity(options.opacity); if (typeof(options.rotation)=='number') this.setRotation(options.rotation); this.renderPhoto_(); }; ol.inherits(ol.style.Photo, ol.style.RegularShape); /** * Clones the style. * @return {ol.style.Photo} */ ol.style.Photo.prototype.clone = function() { return new ol.style.Photo( { stroke: this.stroke_, fill: this.fill_, shadow: this.shadow_, crop: this.crop_, crossOrigin: this.crossOrigin_, kind: this.kind_, radius: this.radius_, src: this.src_, offsetX: this.offset_[0], offsetY: this.offset_[1], opacity: this.getOpacity(), rotation: this.getRotation() }); }; /** * Draws a rounded rectangle using the current state of the canvas. * Draw a rectangle if the radius is null. * @param {Number} x The top left x coordinate * @param {Number} y The top left y coordinate * @param {Number} width The width of the rectangle * @param {Number} height The height of the rectangle * @param {Number} radius The corner radius. */ CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) { if (!r) this.rect(x,y,w,h); else { if (w < 2 * r) r = w / 2; if (h < 2 * r) r = h / 2; this.beginPath(); this.moveTo(x+r, y); this.arcTo(x+w, y, x+w, y+h, r); this.arcTo(x+w, y+h, x, y+h, r); this.arcTo(x, y+h, x, y, r); this.arcTo(x, y, x+w, y, r); this.closePath(); } return this; } /** * Draw the form without the image * @private */ ol.style.Photo.prototype.drawBack_ = function(context, color, strokeWidth) { var canvas = context.canvas; context.beginPath(); context.fillStyle = color; context.clearRect(0, 0, canvas.width, canvas.height); switch (this.kind_) { case 'square': context.rect(0,0,canvas.width-this.shadow_, canvas.height-this.shadow_); break; case 'circle': context.arc(this.radius_+strokeWidth, this.radius_+strokeWidth, this.radius_+strokeWidth, 0, 2 * Math.PI, false); break; case 'folio': var offset = 6; strokeWidth -= offset; context.strokeStyle = 'rgba(0,0,0,0.5)'; var w = canvas.width-this.shadow_-2*offset; var a = Math.atan(6/w); context.save(); context.rotate(-a); context.translate(-6,2); context.beginPath(); context.rect(offset,offset,w,w); context.stroke(); context.fill(); context.restore(); context.save(); context.translate(6,-1); context.rotate(a); context.beginPath(); context.rect(offset,offset,w,w); context.stroke(); context.fill(); context.restore(); context.beginPath(); context.rect(offset,offset,w,w); context.stroke(); break; case 'anchored': context.roundRect(this.sanchor_/2,0,canvas.width-this.sanchor_-this.shadow_, canvas.height-this.sanchor_-this.shadow_, strokeWidth); context.moveTo(canvas.width/2-this.sanchor_-this.shadow_/2,canvas.height-this.sanchor_-this.shadow_); context.lineTo(canvas.width/2+this.sanchor_-this.shadow_/2,canvas.height-this.sanchor_-this.shadow_); context.lineTo(canvas.width/2-this.shadow_/2,canvas.height-this.shadow_);break; default: /* roundrect */ context.roundRect(0,0,canvas.width-this.shadow_, canvas.height-this.shadow_, strokeWidth); break; } context.closePath(); } /** * @private */ ol.style.Photo.prototype.renderPhoto_ = function() { var strokeStyle; var strokeWidth = 0; if (this.stroke_) { strokeStyle = ol.color.asString(this.stroke_.getColor()); strokeWidth = this.stroke_.getWidth(); } var canvas = this.getImage(); // Draw hitdetection image var context = this.hitDetectionCanvas_.getContext('2d'); this.drawBack_(context,"#000",strokeWidth); context.fill(); // Draw the image context = canvas.getContext('2d'); this.drawBack_(context,strokeStyle,strokeWidth); // Draw a shadow if (this.shadow_) { context.shadowColor = 'rgba(0,0,0,0.5)'; context.shadowBlur = this.shadow_/2; context.shadowOffsetX = this.shadow_/2; context.shadowOffsetY = this.shadow_/2; } context.fill(); context.shadowColor = 'transparent'; var self = this; var img = this.img_ = new Image(); if (this.crossOrigin_) img.crossOrigin = this.crossOrigin_; img.src = this.src_; // Draw image if (img.width) self.drawImage_(img); else img.onload = function() { self.drawImage_(img); // Force change (?!) // self.setScale(1); if (self.onload_) self.onload_(); }; // Set anchor var a = this.getAnchor(); a[0] = (canvas.width - this.shadow_)/2; a[1] = (canvas.height - this.shadow_)/2; if (this.sanchor_) { a[1] = canvas.height - this.shadow_; } } /** * Draw an timage when loaded * @private */ ol.style.Photo.prototype.drawImage_ = function(img) { var canvas = this.getImage(); // Remove the circle on the canvas var context = (canvas.getContext('2d')); var strokeWidth = 0; if (this.stroke_) strokeWidth = this.stroke_.getWidth(); var size = 2*this.radius_; context.save(); if (this.kind_=='circle') { context.beginPath(); context.arc(this.radius_+strokeWidth, this.radius_+strokeWidth, this.radius_, 0, 2 * Math.PI, false); context.clip(); } var s, x, y, w, h, sx, sy, sw, sh; // Crop the image to a square vignette if (this.crop_) { s = Math.min (img.width/size, img.height/size); sw = sh = s*size; sx = (img.width-sw)/2; sy = (img.height-sh)/2; x = y = 0; w = h = size+1; } // Fit the image to the size else { s = Math.min (size/img.width, size/img.height); sx = sy = 0; sw = img.width; sh = img.height; w = s*sw; h = s*sh; x = (size-w)/2; y = (size-h)/2; } x += strokeWidth + this.sanchor_/2; y += strokeWidth; context.drawImage(img, sx, sy, sw, sh, x, y, w, h); context.restore(); // Draw a circle to avoid aliasing on clip if (this.kind_=='circle' && strokeWidth) { context.beginPath(); context.strokeStyle = ol.color.asString(this.stroke_.getColor()); context.lineWidth = strokeWidth/4; context.arc(this.radius_+strokeWidth, this.radius_+strokeWidth, this.radius_, 0, 2 * Math.PI, false); context.stroke(); } } /** * @inheritDoc */ ol.style.Photo.prototype.getChecksum = function() { var strokeChecksum = (this.stroke_!==null) ? this.stroke_.getChecksum() : '-'; var fillChecksum = (this.fill_!==null) ? this.fill_.getChecksum() : '-'; var recalculate = (this.checksums_===null) || (strokeChecksum != this.checksums_[1] || fillChecksum != this.checksums_[2] || this.radius_ != this.checksums_[3]); if (recalculate) { var checksum = 'c' + strokeChecksum + fillChecksum + ((this.radius_ !== void 0) ? this.radius_.toString() : '-'); this.checksums_ = [checksum, strokeChecksum, fillChecksum, this.radius_]; } return this.checksums_[0]; };
做扩展的目的主要是为了以后少写几行代码,其实原生的方式也是可以实现该效果的。
var vectorSource = new ol.source.Vector({ url:"data/capital.geojson", format: new ol.format.GeoJSON() }); var vector = new ol.layer.Vector({ source: vectorSource, style: styleFunc }); map.addLayer(vector); var id = 0; map.on("pointermove", function (e) { if(map.hasFeatureAtPixel(e.pixel)){ map.getTargetElement().style.cursor = 'pointer'; } else { map.getTargetElement().style.cursor = 'default'; } }); map.on("click", function (e) { if(map.hasFeatureAtPixel(e.pixel)){ var features = map.getFeaturesAtPixel(e.pixel); var img = features[0].get('img'); document.getElementById('photo').setAttribute('src', img); id = features[0].get('id'); vector.setStyle(styleFunc); } }); function styleFunc (feature){ // var src = 'http://img18.3lian.com/d/file/201712/20/414dc24ceba7436ac6895d9e413ed2cc.png'; var src = feature.get("img"); return new ol.style.Style ({ image: new ol.style.Photo({ src: src, radius: 25, shadow: 2, kind: 'anchored', //default,square,circle,anchored,folio onload: function() { vector.changed(); }, stroke: new ol.style.Stroke({ width: 3, color: id ===feature.get('id') ? '#ffbcc8' : '#ffffff' }) }) }) }
技术博客
CSDN:http://blog.csdn.NET/gisshixisheng
在线教程
https://edu.csdn.net/course/detail/799
https://edu.csdn.net/course/detail/7471
联系方式
类型 | 内容 |
---|---|
1004740957 | |
公众号 | lzugis15 |
[email protected] | |
webgis群 | 452117357 |
Android群 | 337469080 |
GIS数据可视化群 | 458292378 |