Cesuim基础用法(Vue3 + Pinna)
1.1确认一张地图只需要
1.1定义状态变量
1.viewer (Cesium视图对象)
2.tilesets 链接数组
3.entities 实体集合
4.clusterPrimitives 聚合原始集
-
设置视图:
setViewer(v: any):设置 Cesium 视图对象。
-
热力图管理:
addHeatMap(data: any, zoom: boolean = true):添加热力图,支持设置是否缩放到热力图层。removeHeatMap():移除所有热力图。
-
点的管理:
addPoint(data: any):在地图上添加一个点,使用经纬度和图像。removePoint(id: any):根据 ID 移除指定的点。removeAllPoint():清空所有点。
-
GeoJSON 数据管理:
addGeoJson(geojson: any, param: any):加载 GeoJSON 数据并添加到视图中。removeGeoJson():移除所有 GeoJSON 数据源。
-
聚合点的管理:
formatClusterPoint(features: any, type: string):格式化聚合点,设置聚合的距离和图标。removeClusterPoint(type: string):移除指定类型的聚合点。
-
3D Tiles 管理:
add3DTile(url: any):添加 3D Tiles 数据。removeTiles():移除所有 3D Tiles。
-
视图重置:
resetViewer(type: '2D' | '3D'):根据类型重置视图为 2D 或 3D。
-
线条管理:
addLine(id: string, positions: any[], category: string, color: Cesium.Color):添加线条。removeAllLines():移除所有线条。removeLinesByCategory(category: string):根据类别移除线条。
-
区域管理:
-
addRegion(id: string, positions: any[], properties: any, color: Cesium.Color):添加区域。 -
removeRegion(id: string):移除指定区域。 -
removeAllRegions():移除所有区域。
-
-
合成图标
import { defineStore } from 'pinia';
import * as Cesium from 'cesium';
import { CesiumHeatmap } from 'cesium-heatmap-es6';
import PrimitiveCluster from '@/utils/cesium/primitiveCluster.js';
import { ref } from 'vue';
export const useCesiumStore = defineStore('cesium', () => {
let viewer: any = null;
const tilesets: any = [];
const heatmaps: any = [];
const entities: any = {};
const clusterPrimitives: any = {};
const isLoading = ref(false);
const setViewer = (v: any) => {
viewer = v;
};
const addHeatMap = (data: any, zoom: boolean = true) => {
const heatmapLayer = new CesiumHeatmap(viewer, {
zoomToLayer: zoom,
points: data,
heatmapOptions: {
radius: 5,
maxOpacity: 1,
minOpacity: 0
},
heatmapDataOptions: {
max: 300,
min: 0
}
});
heatmaps.push(heatmapLayer);
};
const removeHeatMap = () => {
heatmaps.forEach((heatmap: any) => {
heatmap.remove();
});
};
const addPoint = (data: any) => {
const entity = viewer?.entities.add({
position: Cesium.Cartesian3.fromDegrees(data.lng, data.lat, 30),
billboard: {
image: data.img,
scale: data.scale || 1.0,
width: 30,
height: 40,
pixelOffset: new Cesium.Cartesian2(0, -20)
},
properties: {
data: data.data
}
});
if (entities[data.id]) {
entities[data.id].push(entity);
} else {
entities[data.id] = [entity];
}
};
const removePoint = (id: any) => {
if (entities[id]) {
entities[id].forEach((entity: any) => {
viewer?.entities.remove(entity);
});
}
};
// 清空所有点
const removeAllPoint = () => {
Object.keys(entities).forEach(id => {
entities[id].forEach((entity: any) => {
viewer?.entities.remove(entity);
});
});
// 清空 entities 对象
for (const id in entities) {
if (entities.hasOwnProperty(id)) {
delete entities[id];
}
}
};
const addGeoJson = (
geojson: any,
param = { stroke: Cesium.Color.HOTPINK, fill: Cesium.Color.PINK, strokeWidth: 5, markerSymbol: '', markerSize: 0 }
) => {
Cesium.GeoJsonDataSource.load(geojson, param).then((dataSource: any) => {
viewer?.dataSources.add(dataSource);
// 可以调整视角来适应加载的数据
viewer.flyTo(dataSource);
});
};
const removeGeoJson = () => {
viewer?.dataSources.removeAll();
};
const formatClusterPoint = (features: any, type: string) => {
const primitivesCollection = new Cesium.PrimitiveCollection();
const billboardsCollectionCombine = new Cesium.BillboardCollection();
const primitivecluster: any = new PrimitiveCluster();
//与entitycluster相同设置其是否聚合 以及最大最小值
primitivecluster.enabled = true;
primitivecluster.pixelRange = 30;
primitivecluster.minimumClusterSize = 2;
// primitivecluster._pointCollection = pointCollection;
// primitivecluster._labelCollection = labelCollection;
//后面设置聚合的距离及聚合后的图标颜色显示与官方案例一样
for (let i = 0; i < features.length; i++) {
const feature = features[i];
const coordinates = feature.geometry.coordinates;
const position = Cesium.Cartesian3.fromDegrees(coordinates[0], coordinates[1], 100);
// 带图片的点
billboardsCollectionCombine.add({
id: feature.properties,
image: import.meta.env.BASE_URL + `/texture/${type}.png`,
width: 32,
height: 40,
position
});
}
primitivecluster._billboardCollection = billboardsCollectionCombine;
// 同时在赋值时调用_initialize方法
primitivecluster._initialize(viewer.scene);
primitivesCollection.add(primitivecluster);
const clusterPrimitive = viewer.scene.primitives.add(primitivesCollection);
clusterPrimitives[type] = clusterPrimitive;
primitivecluster.clusterEvent.addEventListener((clusteredEntities: any, cluster: any) => {
// console.log('clusteredEntities', clusteredEntities)
// console.log('cluster', cluster)
// 关闭自带的显示聚合数量的标签
cluster.label.show = false;
cluster.billboard.clusterNumber = clusteredEntities.length;
cluster.billboard.clusterData = JSON.parse(JSON.stringify(clusteredEntities));
cluster.billboard.show = true;
cluster.billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY;
cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
// 根据聚合数量的多少设置不同层级的图片以及大小
if (clusteredEntities.length >= 100) {
cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-5.png`, clusteredEntities.length, 64);
} else if (clusteredEntities.length >= 50) {
cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-3.png`, clusteredEntities.length, 64);
} else if (clusteredEntities.length >= 10) {
cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-2.png`, clusteredEntities.length, 64);
} else {
cluster.billboard.image = combineIconAndLabel(import.meta.env.BASE_URL + `/texture/cluster-1.png`, clusteredEntities.length, 64);
}
// cluster.billboard.image = "/images/school-icon.png";
cluster.billboard._imageHeight = 60;
cluster.billboard._imageWidth = 60;
cluster.billboard._dirty = false;
cluster.billboard.width = 60;
cluster.billboard.height = 60;
});
return primitivecluster;
};
/**
* @description: 将图片和文字合成新图标使用(参考Cesium源码)
* @param {*} url:图片地址
* @param {*} label:文字
* @param {*} size:画布大小
* @return {*} 返回canvas
*/
function combineIconAndLabel(url: string, label: string, size: number) {
// 创建画布对象
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx: any = canvas.getContext('2d');
// @ts-ignore
const promise = new Cesium.Resource.fetchImage(url).then((image: any) => {
// 异常判断
try {
ctx.drawImage(image, 0, 0, size, size);
} catch (e) {
console.log(e);
}
// 渲染字体
// font属性设置顺序:font-style, font-variant, font-weight, font-size, line-height, font-family
ctx.fillStyle = Cesium.Color.WHITE.toCssColorString();
ctx.font = 'bold 20px Microsoft YaHei';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(label, size / 2, size / 2);
return canvas;
});
return promise;
}
const removeClusterPoint = (type: string) => {
viewer?.scene.primitives.remove(clusterPrimitives[type]);
};
const add3DTile = async (url: any) => {
const tileset = await Cesium.Cesium3DTileset.fromUrl(url);
viewer?.scene.primitives.add(tileset);
tilesets.push(tileset);
// viewer.flyTo(tileset)
};
const removeTiles = () => {
tilesets.forEach((item: any) => {
viewer?.scene.primitives.remove(item);
});
tilesets.length = 0;
};
const resetViewer = (type: '2D' | '3D') => {
if (type === '2D') {
viewer?.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(114.25859171304447, 30.606768727988367, 200000)
});
removeTiles();
viewer.scene.morphTo2D(0);
}
if (type === '3D') {
viewer.scene.morphTo3D(0);
viewer?.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(114.25859171304447, 30.606768727988367, 485),
orientation: {
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-30.0),
roll: 0.0
}
});
}
};
let lines: any = {}; // 用于存储不同类别的线条
const addLine = (id: string, positions: any[], category: string, color: Cesium.Color = Cesium.Color.RED) => {
const polyline = viewer?.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray(positions),
width: 2,
material: Cesium.Color.BLUE,
}
});
if (!lines[category]) {
lines[category] = [];
}
lines[category].push({ id, entity: polyline });
};
//删除所有线
const removeAllLines = () => {
Object.keys(lines).forEach(category => {
lines[category].forEach((line: any) => {
viewer?.entities.remove(line.entity);
});
});
lines = {};
};
const removeLinesByCategory = (category: string) => {
if (lines[category]) {
lines[category].forEach((line: any) => {
viewer?.entities.remove(line.entity);
});
delete lines[category];
}
};
let regions: any = {}; // 用于存储不同的区域
const addRegion = (id: string, positions: any[], properties: any, color: Cesium.Color = Cesium.Color.ORANGE) => {
let area = Cesium.Cartesian3.fromDegreesArray(positions);
const regionEntity = viewer?.entities.add({
polygon: {
hierarchy: new Cesium.PolygonHierarchy(area),
material: Cesium.Color.ORANGE.withAlpha(0.4),
outline: true,
outlineWidth: 4,
outlineColor: Cesium.Color.WHITE,
},
properties: properties
});
// viewer?.camera.flyTo({
// destination: Cesium.Cartesian3.fromDegrees(positions[0], positions[1], 1000)
// });
regions[id] = regionEntity;
};
// 删除指定区域图层
const removeRegion = (id: string) => {
if (regions[id]) {
viewer?.entities.remove(regions[id]);
delete regions[id];
}
};
// 删除所有区域
const removeAllRegions = () => {
Object.keys(regions).forEach(id => {
viewer?.entities.remove(regions[id]);
delete regions[id];
});
};
return {
viewer,
isLoading,
add3DTile,
removeTiles,
addLine,
removeAllLines,
addRegion,
removeAllRegions,
removeRegion,
removeLinesByCategory,
setViewer,
addHeatMap,
removeHeatMap,
addPoint,
removePoint,
removeAllPoint,
formatClusterPoint,
removeClusterPoint,
resetViewer,
addGeoJson,
removeGeoJson
};
});
index.vue
<template>
<div id="cesiumcontainer" ></div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import * as Cesium from 'cesium'
import { useCesiumStore } from '@/store/modules/cesiummap'
import { bus } from '@/utils/mitt'
const cesiumStore = useCesiumStore()
onMounted(()=>{
Cesium.Ion.defaultAccessToken = 'xxxxxx'
cesiumStore.isLoading = true
const MODE = import.meta.env.MODE
let layer = null
if(MODE == 'development')
{
layer = newCesium.UrlTemplateImagerProvider({
url:'xxxx',
minimumLevel:3,
maximumLevel:18
})
}else{
layer = new Cesium.WebMapTileServiceImageryProvider({
})
}
const viewer = new Cesium.Viewer('cesiumcontainer',{
timeline:false,
animation:false,
baseLayerPicker:false,
geocoder:false,
homeButtom:fasle,
navigationHelpButtom:false,
infoBox:fasle,
})
viewer.scene.morphTo2D(0);
viewer.imagerLayers.addImgeryProvider(layer)
cesiumStore.setViewer(viewer)
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(114.25859171304447, 30.606768727988367, 200000)
})
const loadEnd=(e:any)=>{
if(e===0)
{
cesiumStore.isLoading = false
bus.$emit('cesiumReady',viewer)
viewer.scene.globe.tileLoadProgressEvent.removeEventListener(loadEnd)
}
}
viewer.scene.globe.tileLoadProgressEvent.addEventListener(loadEnd)
viewer.scene.screenSpaceCameraController.minmumZoomDistance = 10
viewer.scene.screenSpaceCameraController.maxmumZoomDistance = 30000
viewer.scene.postProcessStages.fxaa.enabled = false
})
</script>
<style scoped lang="scss">
#cesiumcontainer {
width: 100%;
height: 100%;
:deep(.cesium-viewer-bottom) {
display: none;
}
}
</style>
在别的页面引入,进行出事化渲染,增加交互逻辑等
<cesium-map></cesium-map>
const initMap = (viewer: any) => {
viewerObj.value = viewer;
// 左键点击
viewer.screenSpaceEventHandler.setInputAction((e: any) => {
const pick = viewer.scene.pick(e.position);
if (Cesium.defined(pick) && pick.id && pick.id.id && pick.id.properties) {
const data = pick.id.properties?.data?._value;
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 右键点击事件
viewer.screenSpaceEventHandler.setInputAction((e: any) => {
const pick = viewer.scene.pick(e.position);
if (Cesium.defined(pick) && pick.id && pick.id.id && pick.id.properties) {
} else {
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
};