OpenLayers.js + geotiff.js实现tiff格式图片导出

引言

OpenLayers是一个开源的JavaScript库,用于在Web浏览器中呈现交互式地图。它本身并不提供导出地图为TIFF图片的功能,但可以结合其他库或服务来实现这个功能。一种可能的解决方案是使用Canvas和FileSaver.js库。Canvas可以将网页上的内容渲染为图像,而FileSaver.js库可以将生成的图像保存到本地。在生成TIFF文件上我选择geotiff.js这个库,GeoTIFF.js可以在TIFF图像中嵌入地理元数据,并且支持多种地图投影坐标系。

步骤

  1. 下载所需要的库
npm install geotiff
npm install file-saver
  1. 获取canvas信息
    这一步可以参考openlayers导出PNG的官方示例:https://openlayers.org/en/v6.15.1/examples/export-map.html
    具体思路就是获取地图所在的元素,转化为画布绘制,这里主要提供思路,所以只针对整个显示区域做导出,需要自定义导出范围的朋友可以私信或者评论,我再补充对应的例子,这里以tiff制做为主。
  • 创建地图
let map = new Map({
    target: 'olMap',
    layers: [layers],
    controls: defaults({
        attributionOptions: {
            collapsible: false,
        }
    }),
    view: new View({
        center: fromLonLat([0,0]),
        zoom: 15
    })
})
  • 获取地图范围
// 获取地图范围信息
let extent = map.getView().calculateExtent(this.map.getSize())
let bottomLeft = [extent[0], extent[1]]
let topRight = [extent[2], extent[3]]
let bbox = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]]
  • 获取投影和分辨率
let resolution = map.getView().getResolution()
let projection = map.getView().getProjection().getCode()
  • 将地图转换为canvas
map.once('postcompose', function (event) {
    let canvas = event.context.canvas;
    let imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data
    })

至此,我们得到了需要导出范围的地图canvas及图像数据,接下来,我们需要将数据写入tiff文件中,即需要用到geotiff中的writeArrayBuffer方法。

  1. 创建tiff文件实例
  • 导入所需方法
import {writeArrayBuffer} from 'geotiff'
  • 编写元数据
// 将图像数据写入GeoTIFF文件
let tifImage = writeArrayBuffer(imageData, {
    width: canvas.width,
    height: canvas.height,
    GeographicTypeGeoKey: projection,
    ModelTiepoint: [0, 0, 0, bbox[0], bbox[3], 0],
    ModelPixelScale: [resolution, resolution, 0],
    GTModelTypeGeoKey: 2,
})

这里将步骤3中得到的地图元素写入tiff文件,{}中为元数据,这里我依次定义了图片的长宽、投影类型、模型点配准(这里我理解为canvas坐标和地理坐标的配准)、模型像素比例尺、模型类型(0表示未定义或未知;1表示2D投影坐标参考系;2表示地理二维坐标参考系;3表示3D坐标参考系;32767表示自定义)。

这些参数可以查询GEOTIFF文件规范,根据需要添加,我这里只需要图片能导入ArcGIS能显示具体坐标信息即可。

  • geotiff.js源代码展示的属性信息
[
    'Compression',
    'ExtraSamples',
    'GeographicTypeGeoKey',
    'GTModelTypeGeoKey',
    'GTRasterTypeGeoKey',
    'ImageLength', // synonym of ImageHeight
    'ImageWidth',
    'Orientation',
    'PhotometricInterpretation',
    'ProjectedCSTypeGeoKey',
    'PlanarConfiguration',
    'ResolutionUnit',
    'SamplesPerPixel',
    'XPosition',
    'YPosition',
  ]
  1. 导出图片
  • 导入方法
import { saveAs } from 'file-saver'
  • 转blob格式
let tiffBlob = new Blob([tifImage])
  • 导出
saveAs(tiffBlob, 'map.tiff')
  1. 全部代码
import {writeArrayBuffer} from 'geotiff'
import { saveAs } from 'file-saver'

let map = new Map({
    target: 'olMap',
    layers: [layers],
    controls: defaults({
        attributionOptions: {
            collapsible: false,
        }
    }),
    view: new View({
        center: fromLonLat([0,0]),
        zoom: 15
    })
})

// 获取地图范围信息
let extent = map.getView().calculateExtent(this.map.getSize())
let bottomLeft = [extent[0], extent[1]]
let topRight = [extent[2], extent[3]]
let bbox = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]]

let resolution = map.getView().getResolution()
let projection = map.getView().getProjection().getCode()

map.once('postcompose', function (event) {
    let canvas = event.context.canvas;
    let imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data
    
    // 将图像数据写入GeoTIFF文件
    let tifImage = writeArrayBuffer(imageData, {
        width: canvas.width,
        height: canvas.height,
        GeographicTypeGeoKey: projection,
        ModelTiepoint: [0, 0, 0, bbox[0], bbox[3], 0],
        ModelPixelScale: [resolution, resolution, 0],
        GTModelTypeGeoKey: 2,
    })
    
    let tiffBlob = new Blob([tifImage])
    saveAs(tiffBlob, 'map.tiff')
})

参考资料

  • GeoTiff格式说明:



  • geotiff的github链接:https://github.com/geotiffjs/geotiff.js/
    官方文档:https://geotiffjs.github.io/geotiff.js/module-geotiff.html