/* Copyright (c) 2015 Jean-Marc VIGLINO, released under the CeCILL-B license (French BSD license) (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt). ol_source_GeoImage is a layer source with georeferencement to place it on a map. */ /** @typedef {Object} GeoImageOptions * @property {url} url url of the static image * @property {image} image the static image, if not provided, use url to load an image * @property {ol.Coordinate} imageCenter coordinate of the center of the image * @property {ol.Size|number} imageScale [scalex, scaley] of the image * @property {number} imageRotate angle of the image in radian, default 0 * @property {ol.Extent} imageCrop of the image to be show (in the image) default: [0,0,imageWidth,imageHeight] * @property {Array.} imageMask linestring to mask the image on the map */ import ol_source_ImageCanvas from 'ol/source/ImageCanvas.js' import ol_geom_Polygon from 'ol/geom/Polygon.js' import {boundingExtent as ol_extent_boundingExtent} from 'ol/extent.js' import {fromExtent as ol_geom_Polygon_fromExtent} from 'ol/geom/Polygon.js' /** Layer source with georeferencement to place it on a map * @constructor * @extends {ol_source_ImageCanvas} * @param {GeoImageOptions} options */ var ol_source_GeoImage = class olsourceGeoImage extends ol_source_ImageCanvas { constructor(opt_options) { var options = { attributions: opt_options.attributions, logo: opt_options.logo, projection: opt_options.projection } // Draw image on canvas options.canvasFunction = function (extent, resolution, pixelRatio, size) { return this.calculateImage(extent, resolution, pixelRatio, size) } super(options) // options.projection = opt_options.projection; // Load Image this._image = (opt_options.image ? opt_options.image : new Image) this._image.crossOrigin = opt_options.crossOrigin // 'anonymous'; // Show image on load this._image.onload = function () { this.setCrop(this.crop) this.changed() }.bind(this) if (!opt_options.image) this._image.src = opt_options.url // Coordinate of the image center this.center = opt_options.imageCenter // Image scale this.setScale(opt_options.imageScale) // Rotation of the image this.rotate = opt_options.imageRotate ? opt_options.imageRotate : 0 // Crop of the image this.crop = opt_options.imageCrop // Mask of the image this.mask = opt_options.imageMask // Crop this.setCrop(this.crop) // Calculate extent on change this.on('change', function () { this.set('extent', this.calculateExtent()) }.bind(this)) } /** calculate image at extent / resolution * @param {ol/extent/Extent} extent * @param {number} resolution * @param {number} pixelRatio * @param {ol/size/Size} size * @return {HTMLCanvasElement} */ calculateImage(extent, resolution, pixelRatio, size) { if (!this.center) return var canvas = document.createElement('canvas') canvas.width = size[0] canvas.height = size[1] var ctx = canvas.getContext('2d') if (!this._imageSize) return canvas // transform coords to pixel function tr(xy) { return [ (xy[0] - extent[0]) / (extent[2] - extent[0]) * size[0], (xy[1] - extent[3]) / (extent[1] - extent[3]) * size[1] ] } // Clipping mask if (this.mask) { ctx.beginPath() var p = tr(this.mask[0]) ctx.moveTo(p[0], p[1]) for (var i = 1; i < this.mask.length; i++) { p = tr(this.mask[i]) ctx.lineTo(p[0], p[1]) } ctx.clip() } // Draw var pixel = tr(this.center) var dx = (this._image.naturalWidth / 2 - this.crop[0]) * this.scale[0] / resolution * pixelRatio var dy = (this._image.naturalHeight / 2 - this.crop[1]) * this.scale[1] / resolution * pixelRatio var sx = this._imageSize[0] * this.scale[0] / resolution * pixelRatio var sy = this._imageSize[1] * this.scale[1] / resolution * pixelRatio ctx.translate(pixel[0], pixel[1]) if (this.rotate) ctx.rotate(this.rotate) ctx.drawImage(this._image, this.crop[0], this.crop[1], this._imageSize[0], this._imageSize[1], -dx, -dy, sx, sy) return canvas } /** * Get coordinate of the image center. * @return {ol.Coordinate} coordinate of the image center. * @api stable */ getCenter() { return this.center } /** * Set coordinate of the image center. * @param {ol.Coordinate} coordinate of the image center. * @api stable */ setCenter(center) { this.center = center this.changed() } /** * Get image scale. * @return {ol.size} image scale (along x and y axis). * @api stable */ getScale() { return this.scale } /** * Set image scale. * @param {ol.size|Number} image scale (along x and y axis or both). * @api stable */ setScale(scale) { switch (typeof (scale)) { case 'number': scale = [scale, scale] break case 'object': if (scale.length != 2) return break default: return } this.scale = scale this.changed() } /** * Get image rotation. * @return {Number} rotation in radian. * @api stable */ getRotation() { return this.rotate } /** * Set image rotation. * @param {Number} rotation in radian. * @api stable */ setRotation(angle) { this.rotate = angle this.changed() } /** * Get the image. * @api stable */ getGeoImage() { return this._image } /** * Get image crop extent. * @return {ol.extent} image crop extent. * @api stable */ getCrop() { return this.crop } /** * Set image mask. * @param {ol.geom.LineString} coords of the mask * @api stable */ setMask(mask) { this.mask = mask this.changed() } /** * Get image mask. * @return {ol.geom.LineString} coords of the mask * @api stable */ getMask() { return this.mask } /** * Set image crop extent. * @param {ol.extent|Number} image crop extent or a number to crop from original size. * @api stable */ setCrop(crop) { // Image not loaded => get it latter if (!this._image.naturalWidth) { this.crop = crop return } if (crop) { switch (typeof (crop)) { case 'number': crop = [crop, crop, this._image.naturalWidth - crop, this._image.naturalHeight - crop] break case 'object': if (crop.length != 4) return break default: return } crop = ol_extent_boundingExtent([[crop[0], crop[1]], [crop[2], crop[3]]]) this.crop = [Math.max(0, crop[0]), Math.max(0, crop[1]), Math.min(this._image.naturalWidth, crop[2]), Math.min(this._image.naturalHeight, crop[3])] } else this.crop = [0, 0, this._image.naturalWidth, this._image.naturalHeight] if (this.crop[2] <= this.crop[0]) this.crop[2] = this.crop[0] + 1 if (this.crop[3] <= this.crop[1]) this.crop[3] = this.crop[1] + 1 this._imageSize = [this.crop[2] - this.crop[0], this.crop[3] - this.crop[1]] this.changed() } /** Get the extent of the source. * @param {module:ol/extent~Extent} extent If provided, no new extent will be created. Instead, that extent's coordinates will be overwritten. * @return {ol.extent} */ getExtent(opt_extent) { var ext = this.get('extent') if (!ext) ext = this.calculateExtent() if (opt_extent) { for (var i = 0; i < opt_extent.length; i++) { opt_extent[i] = ext[i] } } return ext } /** Calculate the extent of the source image. * @param {boolean} usemask return the mask extent, default return the image extent * @return {ol.extent} */ calculateExtent(usemask) { var polygon if (usemask !== false && this.getMask()) { polygon = new ol_geom_Polygon([this.getMask()]) } else { var center = this.getCenter() var scale = this.getScale() var width = this.getGeoImage().width * scale[0] var height = this.getGeoImage().height * scale[1] var extent = ol_extent_boundingExtent([ [center[0] - width / 2, center[1] - height / 2], [center[0] + width / 2, center[1] + height / 2] ]) polygon = ol_geom_Polygon_fromExtent(extent) polygon.rotate(-this.getRotation(), center) } var ext = polygon.getExtent() return ext } } export default ol_source_GeoImage