38.在 Vue 3 中使用 OpenLayers 导出地图为 PDF

在现代 web 应用开发中,地图显示和数据可视化已经成为许多应用的核心功能。OpenLayers 是一个强大的开源 JavaScript 库,它为开发者提供了丰富的地图绘制功能。今天,我们将介绍如何在 Vue 3 中使用 OpenLayers,并实现一个非常实用的功能:将地图导出为 PDF 文件。

背景

OpenLayers 允许我们创建互动式地图,并支持多种地图服务,如瓦片地图(TileLayer)、矢量地图(VectorTileLayer)等。Vue 3 引入的 Composition API 提供了更灵活和清晰的方式来管理组件的状态和行为。我们将在 Vue 3 中使用 OpenLayers,并结合 jsPDF 库来实现导出地图为 PDF 的功能。

技术栈

  • Vue 3:构建应用的框架,采用 Composition API。
  • OpenLayers:用于展示地图和加载不同类型的地图数据。
  • jsPDF:用于将地图导出为 PDF 文件。

实现步骤

1. 安装依赖

首先,我们需要安装 openlayersjspdf 两个依赖。可以使用 npm 或 yarn 来安装:

javascript 复制代码
npm install ol jspdf

2. 配置 Vue 组件

在我们的 Vue 组件中,我们将创建一个地图,允许用户点击按钮将当前地图导出为 PDF。

完整代码
javascript 复制代码
<!--
 * @Author: 彭麒
 * @Date: 2024/12/20
 * @Email: 1062470959@qq.com
 * @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。
 -->
<template>
  <button class="back-button" @click="goBack">返回</button>
  <div class="container">
    <h3>vue+openlayers: 导出地图(pdf)</h3>
    <h4>
      <el-button type="primary" @click="exportPDF" size="small">导出 pdf 地图</el-button>
    </h4>
    <div id="vue-openlayers"></div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'; // 引入 Vue 3 的 ref 和 onMounted 钩子
import 'ol/ol.css'; // 引入 OpenLayers 样式
import Map from 'ol/Map'; // 引入 OpenLayers 的 Map 类
import View from 'ol/View'; // 引入 OpenLayers 的 View 类
import TileLayer from 'ol/layer/Tile'; // 引入 OpenLayers 的 TileLayer 类
import OSM from 'ol/source/OSM'; // 引入 OpenLayers 的 OSM 数据源类
import WMTSTileGrid from 'ol/tilegrid/WMTS'; // 引入 OpenLayers 的 WMTSTileGrid 类
import VectorTileLayer from 'ol/layer/VectorTile'; // 引入 OpenLayers 的 VectorTileLayer 类
import VectorTile from 'ol/source/VectorTile'; // 引入 OpenLayers 的 VectorTile 类
import MVT from 'ol/format/MVT'; // 引入 OpenLayers 的 MVT 格式类
import Fill from 'ol/style/Fill'; // 引入 OpenLayers 的 Fill 样式类
import Style from 'ol/style/Style'; // 引入 OpenLayers 的 Style 类
import Circle from 'ol/style/Circle'; // 引入 OpenLayers 的 Circle 样式类
import { jsPDF } from 'jspdf'; // 引入 jsPDF 库
import router from "@/router"; // 引入 Vue Router

const goBack = () => {
  router.push('/OpenLayers'); // 返回到 OpenLayers 页面
};

const map = ref(null); // 定义地图的响应式引用

// 初始化地图
const initMap = () => {
  map.value = new Map({
    layers: [
      new TileLayer({
        source: new OSM(), // 使用 OSM 数据源
      }),
    ],
    target: 'vue-openlayers', // 目标容器 ID
    view: new View({
      center: [0, 0], // 地图中心点
      projection: 'EPSG:4326', // 投影为 EPSG:4326
      zoom: 4, // 初始缩放级别
    }),
  });
};

// 设置样式
const style = () => {
  return new Style({
    image: new Circle({
      radius: 5, // 圆形半径为 5
      fill: new Fill({
        color: 'LawnGreen', // 填充颜色为草绿色
      }),
    }),
  });
};

// 加载 MVT 数据
const readMVT = () => {
  const myLayer = new VectorTileLayer({
    style: style(), // 设置图层样式
    source: new VectorTile({
      visible: true, // 图层可见
      url: 'https://gibs-{a-c}.earthdata.nasa.gov/wmts/epsg4326/best/wmts.cgi?TIME=2020-03-18T00:00:00Z&layer=GRanD_Dams&tilematrixset=2km&Service=WMTS&Request=GetTile&Version=1.0.0&FORMAT=application%2Fvnd.mapbox-vector-tile&TileMatrix={z}&TileCol={x}&TileRow={y}', // 数据源 URL
      format: new MVT(), // 数据格式为 MVT
      projection: 'EPSG:4326', // 投影为 EPSG:4326
      tileGrid: new WMTSTileGrid({
        extent: [-180, -90, 180, 90], // 瓦片网格范围
        resolutions: [0.5625, 0.28125, 0.140625, 0.0703125, 0.03515625, 0.017578125], // 分辨率数组
        tileSize: [512, 512], // 瓦片大小
      }),
    }),
  });
  map.value.addLayer(myLayer); // 将图层添加到地图
};

// 导出地图为 PDF
const exportPDF = () => {
  map.value.once('rendercomplete', () => {
    const mapCanvas = document.createElement('canvas'); // 创建 canvas 元素
    const size = map.value.getSize(); // 获取地图大小

    // 设置尺寸为 A4(297*210),分辨率为 150dpi
    const width = Math.round((297 * 150) / 25.4);
    const height = Math.round((210 * 150) / 25.4);

    mapCanvas.width = width; // 设置 canvas 宽度
    mapCanvas.height = height; // 设置 canvas 高度

    const mapContext = mapCanvas.getContext('2d'); // 获取 canvas 上下文
    Array.prototype.forEach.call(
      map.value.getViewport().querySelectorAll('.ol-layer canvas, canvas.ol-layer'),
      (canvas) => {
        if (canvas.width > 0) {
          const opacity = canvas.parentNode.style.opacity || canvas.style.opacity; // 获取透明度
          mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity); // 设置全局透明度

          const backgroundColor = canvas.parentNode.style.backgroundColor; // 获取背景颜色
          if (backgroundColor) {
            mapContext.fillStyle = backgroundColor; // 设置填充样式
            mapContext.fillRect(0, 0, canvas.width, canvas.height); // 填充背景颜色
          }

          let matrix;
          const transform = canvas.style.transform; // 获取变换样式
          if (transform) {
            matrix = transform
              .match(/^matrix\(([^\(]*)\)$/)[1]
              .split(',')
              .map(Number); // 解析变换矩阵
          } else {
            matrix = [
              parseFloat(canvas.style.width) / canvas.width,
              0,
              0,
              parseFloat(canvas.style.height) / canvas.height,
              0,
              0,
            ]; // 默认变换矩阵
          }

          CanvasRenderingContext2D.prototype.setTransform.apply(mapContext, matrix); // 应用变换矩阵
          mapContext.drawImage(canvas, 0, 0); // 绘制图像
        }
      }
    );
    mapContext.globalAlpha = 1; // 重置全局透明度

    // 导出地图为 PDF
    const pdf = new jsPDF('landscape', undefined, [width, height]);
    pdf.addImage(mapCanvas.toDataURL('image/jpeg'), 'JPEG', 0, 0, width, height);
    pdf.save('map.pdf'); // 保存 PDF 文件
  });
  map.value.renderSync(); // 同步渲染地图
};

// 挂载地图
onMounted(() => {
  initMap(); // 初始化地图
  readMVT(); // 加载 MVT 数据
});
</script>

<style scoped>
.container {
  width: 840px;
  height: 590px;
  margin: 50px auto;
  border: 1px solid #42B983;
}

#vue-openlayers {
  width: 800px;
  height: 400px;
  margin: 0 auto;
  border: 1px solid #42B983;
  position: relative;
}
</style>

3. 代码解析

3.1 初始化地图

setup 函数中,我们使用 ref 声明了 map 变量,后续会用它来保存 OpenLayers 的地图实例。然后,initMap 函数负责初始化地图,并将其显示在页面上的 #vue-openlayers 容器中。

3.2 加载地图图层

我们使用 VectorTileLayer 来加载一个 WMTS 图层(世界大坝数据)。通过配置 OpenLayers 的 VectorTile 来源和瓦片网格,地图会加载来自 NASA Earth Data 的地理信息数据。

3.3 导出地图为 PDF

exportPDF 函数是实现导出功能的核心。在地图渲染完成后,我们通过 rendercomplete 事件来触发导出操作。具体步骤如下:

  1. 创建一个新的 canvas 元素,将地图内容渲染到该 canvas 上。
  2. 使用 jsPDF 将该 canvas 转换为 PDF 并下载。
3.4 样式与布局

我们使用了 scoped 样式,确保样式仅应用于当前组件。在容器 #vue-openlayers 上设置了地图的显示区域,并定义了容器的宽度和高度。

导出后

4. 总结

通过以上步骤,我们成功实现了在 Vue 3 中使用 OpenLayers 加载地图,并通过 jsPDF 库将地图导出为 PDF 文件。这个功能非常适合需要地图展示和分享功能的应用。你可以根据需求调整地图的样式和导出的尺寸,以满足不同的需求。

希望这篇文章能帮助你理解如何在 Vue 3 中使用 OpenLayers 和 jsPDF 实现导出地图为 PDF 的功能!

相关推荐
fmdpenny16 分钟前
Vue3初学之商品的增,删,改功能
开发语言·javascript·vue.js
ARM+FPGA+AI工业主板定制专家25 分钟前
基于RK3576/RK3588+FPGA+AI深度学习的轨道异物检测技术研究
人工智能·深度学习
小美的打工日记28 分钟前
ES6+新特性,var、let 和 const 的区别
前端·javascript·es6
helianying5537 分钟前
云原生架构下的AI智能编排:ScriptEcho赋能前端开发
前端·人工智能·云原生·架构
@PHARAOH44 分钟前
HOW - 基于master的a分支和基于a的b分支合流问题
前端·git·github·分支管理
涔溪1 小时前
有哪些常见的 Vue 错误?
前端·javascript·vue.js
程序猿online1 小时前
前端jquery 实现文本框输入出现自动补全提示功能
前端·javascript·jquery
小猪咪piggy2 小时前
【深度学习入门】深度学习知识点总结
人工智能·深度学习
2401_897579652 小时前
ChatGPT接入苹果全家桶:开启智能新时代
前端·chatgpt
DoraBigHead2 小时前
JavaScript 执行上下文:一场代码背后的权谋与博弈
前端