ol5里面实现相册地图

概述

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


ol5里面实现相册地图_第1张图片
相册地图

关键点

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

实现效果

ol5里面实现相册地图_第2张图片
相册地图
ol5里面实现相册地图_第3张图片
选中后放大

实现

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'
      })
    })
  })
}   

技术博客
CSDN:http://blog.csdn.NET/gisshixisheng
在线教程
https://edu.csdn.net/course/detail/799
https://edu.csdn.net/course/detail/7471
联系方式

类型 内容
qq 1004740957
公众号 lzugis15
e-mail [email protected]
webgis群 452117357
Android群 337469080
GIS数据可视化群 458292378
ol5里面实现相册地图_第4张图片
LZUGIS

你可能感兴趣的:(ol5里面实现相册地图)