专栏首页跟牛老师一起学WEBGISol5里面实现相册地图

ol5里面实现相册地图

概述

如下图,在手机里面有一个这样的功能,我称之为“相册地图”,本文讲述的是通过扩展ol.style的类,来实现“相册地图”这个功能。

关键点

要实现这个功能有两个关键点:1、地图聚合;2、图片样式。有关地图聚类的在很早之前的文章里面已经涉及到过,所以本文重点讲述图片样式。

实现效果

实现

1、扩展ol.style.Photo
/**
 * @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];
};

做扩展的目的主要是为了以后少写几行代码,其实原生的方式也是可以实现该效果的。

2、调用
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'
      })
    })
  })
}	

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Openlayers2卷帘功能的实现

    在WebGIS开发中,经常会有用户提需求,要实现卷帘功能,卷帘功能主要是实现两张图之间的对比。在前文中,讲到了openlayers3以及Arcgis for j...

    lzugis
  • Openlayers离线加载天地图

    经过一个春节的休整,今天终于开始了!不论什么时候,都不要忘记学习,学习是一辈子的事情!今天,我来说说如何实现天地图的离线以及Openlayers加载离线数据实现...

    lzugis
  • Arcgis for Js之Graphiclayer扩展详解

    在前两节,讲到了两种不同方式的聚类,一种是基于距离的,一种是基于区域范围的,两种不同的聚类都是通过扩展esri/layers/GraphicsLayer方法来实...

    lzugis
  • Flutter基础widgets教程-TabBar篇

    青年码农
  • Canvas游动的花花肠子

    我不是费圆
  • 一个基于vue的图片轮播组件的实现

    4.3 组件挂在后,检查autoplay属性,若该属性为true,则产生一个计时器

    前端博客 : alili.tech
  • 商品多种规格属性的选择(sku 算法)

    如上图中每一个单规格选项,例如==珍珠白==、==12GB+512GB==、==不分期==就是一个规格(sku)。商品和 sku 属于一对多的关系,也就是我们可...

    Krry
  • 数据结构与JS也可以成为CP(十)Graph图

    1)深度优先搜索算法比较简单:访问一个没有访问过的顶点,将它标记为已访问,再递归地去访问在初始顶点的邻接表中其他没有访问过的顶点。

    萌兔IT
  • JavaScript 关于this的几道面试题及介绍

    与其他语言相比,函数的this关键字在JavaScript中的行为略有不同。并且它在严格模式和非严格模式之间也有一些区别。

    前端博客 : alili.tech
  • Flutter基础widgets教程-Tooltip篇

    青年码农

扫码关注云+社区

领取腾讯云代金券