大家好!从今天开始,我将分享我在GIS开发的过程中如何利用Openlayers和Cesium来创建一个二三维联动的项目,在后面的文章中我会讲解整个项目的搭建过程及每一个功能模块的实现方法,其中包括了很多我在入行几年做的各种特色功能实现方法,我也希望跟大家一起探讨学习,共同进步。
执行命令:vue create 项目名
E:\WebProject>vue create threedview
Vue CLI v5.0.8
? Please pick a preset:
Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
> Manually select features//选择自定义
#下面是选择后的步骤
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? No
创建完成后,使用命令 npm run serve
启动服务。
1)安装openlayers
npm install ol --save
2)安装cesium
npm install cesium --save
3)安装olcs
npm i --save olcs
olcs是OpenLayers-Cesium 集成库,主要是将OpenLayers与Cesium的二三维功能进行集成联动,可以实现二三维地图无缝切换。
在 2D 和 3D 之间平滑切换并同步主要内容:
在创建完Vue项目后,我们在src下我们创建一个plugins文件夹,这个文件夹我们用来管理针对地图的所有文件结构,我们后面所有的功能类都在此文件夹下,这样做也是为后面我们统一打包封装组件打下一个基础。
main.js
SworldMap.js
main.js
,作为所有类对象的接口管理类,后面我们所以的接口模块全都通过这个类出去。
import SworldMap from "./SworldMap";
export {
SworldMap
}
SworldMap.js
地图管理类,管理地图视图的参数等信息,并且包括后续要用到的视图场景、相机、视图动作、动画管理器等对象。
地图加载这一部分的功能,主要以Openlayers与olcs两个类的协同管理来加载与显示整个地图,Cesium我们采用全局配置的方式进行载入,因为要用到Cesium的地方比较多,并且Cesium由于自身过于庞大,所以我们就不用去单独引用源代码来进行开发,如果引用源代码的话,这会增加后期我们整个系统的加载时间;所以我们下面将Cesium配置为全局对象。
2、在index.html
中添加如下代码,引用Cesium
<script type="text/javascript" src="<%= BASE_URL %>Cesium/Cesium.js">script>
<link rel="stylesheet" href="<%= BASE_URL %>Cesium/Widgets/widgets.css">
3、配置完全局Cesium后,就要在Vue中放置一个用于加载地图的容器,为容器添加属性 id=“map”,在script标签中我们引用我们的main文件,并实例化Sworldmap。
HelloWorld.vue
<template>
<div id="map">
div>
template>
<script>
import { onMounted, onUnmounted } from "@vue/runtime-core";
import * as Sworld from "../plugins/main";
export default {
name: 'HelloWorld',
props: {
msg: String
},
setup(){
onMounted(() => {
//实例化SworldMap对象
var sworldmap = new Sworld.SworldMap(null);
//调用初始化方法
sworldmap.initMap();
//将地图类绑定到全局,如果其他地方需要使用就可以只去window中取就可以了
window.sworldmap = sworldmap;
});
}
}
script>
<style scoped lang="scss">
#map {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 999;
}
style>
4、二三维地图的引用及加载,在下面的类的构造函数中,我们先创建了一个地图底图图层组,然后将我们默认的天地图影像瓦片加载到图层组中;接着调用初始化地图方法的时候,我们就可以将图层组加载到地图图层中了。
SworldMap.js
import { Map, View, Collection } from "ol";
import * as proj from 'ol/proj';
import { Group, Tile } from "ol/layer"
import * as control from 'ol/control';
import OLCesium from 'olcs/OLCesium.js';
import * as tilegrid from 'ol/tilegrid';
import TGWMTS from 'ol/tilegrid/WMTS';
import { WMTS } from 'ol/source';
import * as extent from 'ol/extent';
function SworldMap(options) {
//二维地图初始化地图
this._base2DLayer = new Group({
layers: []
});
this._base2DLayer.name = '地图底图图层组';
//底图标签
this._base2DLayer.isBaseLayer = true;
//默认为三维地图
this.isD3Map = false;
}
/**
* 获取瓦片源数据对象
* @param {object} urlModel 瓦片参数
* @returns
*/
SworldMap.prototype.getSource = function (urlModel) {
var source = null;
var projection = proj.get('EPSG:3857');
if (urlModel.project) {
projection = proj.get(urlModel.project);
}
//切片方案,有配置最大或最小比例层级时,定制对应的切片方案,只显示那几级
var tileGrid = (urlModel.maxZoom !== undefined || urlModel.minZoom !== undefined) ?
tilegrid.createXYZ({
extent: projection.getExtent(),
maxZoom: urlModel.maxZoom,
minZoom: urlModel.minZoom,
tileSize: urlModel.tileSize
}) : undefined;
switch (urlModel.type) {
case "WMTS":
var projectionExtent = projection.getExtent();
var size = extent.getWidth(projectionExtent) / 256;
var level_vol = urlModel.zoomVolatility || 0;//级别上下浮动值
var gridName = urlModel.gridName || '';//geoserver gridName
var resolutions = new Array(14);
var matrixIds = new Array(14);
for (var z = 0; z < 18; ++z) {
resolutions[z] = size / Math.pow(2, z);
matrixIds[z] = gridName + (z + level_vol);
}
source = new WMTS({
url: urlModel.url,
layer: urlModel.layer,
matrixSet: urlModel.matrixSet,
format: urlModel.format,
projection: urlModel.projection || projection,
tileGrid: urlModel.tileGrid || new TGWMTS({
origin: extent.getTopLeft(projectionExtent),
resolutions: resolutions,
matrixIds: matrixIds
}),
style: urlModel.style,
wrapX: true,
crossOrigin: 'anonymous',
tileLoadFunction: urlModel.tileLoadFunction,
dimensions: {
'tk': urlModel.tk
}
});
break;
}
return source;
}
/**
* 加载地图瓦片图层
*/
SworldMap.prototype._load2DBaseLayers = function () {
var _baseMap2DUrls = [{
name: "天地图WGS84卫星影像图",
url: 'http://t{0-7}.tianditu.gov.cn/img_c/wmts?',
layer: 'img',
matrixSet: 'c',
format: 'tiles',
style: 'default',
maxZoom: this._mapMaxZoom,
type: 'WMTS',
project: "EPSG:4326",
tk: '您的天地图Token'
}, {
name: "天地图WGS84卫星影像标签",
url: 'http://t{0-7}.tianditu.gov.cn/cia_c/wmts?',
layer: 'cia',
matrixSet: 'c',
format: 'tiles',
style: 'default',
maxZoom: this._labelMaxZoom,
type: 'WMTS',
project: "EPSG:4326",
tk: '您的天地图Token'
}];
//清空已有底图
this._base2DLayer.getLayers().clear();
//添加新底图
var layers = [];
for (var i = 0; i < _baseMap2DUrls.length; i++) {
var dataSource = this.getSource(_baseMap2DUrls[i]);
var tileGrid = dataSource.getTileGrid();
var layer = new Tile({
source: dataSource,
maxZoom: 30
});
layer.name = _baseMap2DUrls[i].name ? _baseMap2DUrls[i].name : "";
layers.push(layer);
}
this._base2DLayer.setLayers(new Collection(layers));
}
SworldMap.prototype.initMap = function () {
this._load2DBaseLayers();
//初始化二维地图
this._d2map = new Map({
controls: control.defaults({
zoom: false,
attribution: false,
rotate: false
}),
layers: [this._base2DLayer],
target: "map",
view: new View({
center: proj.transform([104, 31], 'EPSG:4326', 'EPSG:4326'),
zoom: 14,
projection: 'EPSG:4326',
minZoom: 0,
maxZoom: 18
}),
logo: false,
});
var map3dContainer = null;
//附加三维地图(负责三维地图创建、同步)
this._d3map = new OLCesium({
map: this._d2map,
terrainExaggeration: 0,
target: map3dContainer
});
}
/**
* 二三维切换
* @date 2023-03-30
* @returns {any}
*/
SworldMap.prototype.switchMap = function () {
let b = (this.isD3Map) ? false : true;
this._d3map.setEnabled(b);
}
export default SworldMap;
这里我们就将openlayers与cesium两个地图进行了联动,可以实现顺滑的二三维切换,这样我们封装出来的库既支持二维电子地图,又能满足三维数据的炫酷展示,当然最重要的还是olcs这个库,后面我们也会对olcs的源码进行一个私人化的定制,也是为了更加方便的调用两者的功能。
那么今天的文章就到这里了,觉得还不错的朋友可以用您发财的小手给点个小赞,这里先谢过了!后面的文章我会更加详细的将二三维中常用到的接口都封装起来,尽请关注。