Cesium实现深色地图效果

点击此处预览效果

一、引言

Cesium作为一款强大的开源JavaScript库,为我们提供了创建高性能3D地球和地图的能力。我们可以轻松地构建出功能丰富、交互性强的地图应用。本文将详细介绍一个基于Vue和Cesium的深色地图实现方案,展示如何创建一个充满科技感的地图。

二、项目概述

主要用于创建一个基于Cesium的地图视图,并对地图根据滤镜颜色进行一些设置,以实现深蓝色地图的效果,已达到领导们的需求。

三、模板部分

vue 复制代码
<template> 
    <div id="cesiumContainer" class="fullSize"></div> 
</template> 

在模板部分,我们创建了一个div元素,其idcesiumContainer,类名为fullSize。这个div元素将作为Cesium地图的容器,后续的地图视图将被渲染到这个容器中。

四、脚本部分

4.1 引入依赖

typescript 复制代码
<script setup lang="ts"> 
import { onMounted, nextTick, ref, reactive } from 'vue'; 
import * as Cesium from "cesium"; 
import 'cesium/Source/Widgets/widgets.css'; 
import AmapMercatorTilingScheme from '@/modules/AmapMercatorTilingScheme/AmapMercatorTilingScheme'; 
</script>

这里引入了Vue的一些组合式API函数,如onMountednextTickrefreactive,用于处理组件的生命周期和状态管理。同时,引入了Cesium库和相关的样式文件,以及自定义的AmapMercatorTilingScheme模块,用于支持高德地图的瓦片方案,当然,你也可以使用其他的地图数据作为地图图层。

4.2 配置Cesium

typescript 复制代码
Cesium.Ion.defaultAccessToken = "your token"; 
const viewModel = reactive({ 
    brightness: 0, 
    contrast: 0, 
    hue: 0, 
    saturation: 0, 
    gamma: 0, 
}) // 获取 Cesium 视图对象 
let viewer: Cesium.Viewer | undefined; 
const filterColor = ref('#003690') 
const imageryLayers = ref() 
  • Cesium.Ion.defaultAccessToken:设置Cesium的访问令牌,用于访问Cesium的相关资源。
  • viewModel:使用reactive创建一个响应式对象,用于存储地图的一些属性,如亮度、对比度、色调、饱和度和伽马值。
  • viewer:定义一个变量,用于存储Cesium的视图对象。 - filterColor:使用ref创建一个响应式引用,用于存储地图颜色过滤的初始值。
  • imageryLayers:使用ref创建一个响应式引用,用于存储地图图层信息。

4.3 地图样式设置相关函数

4.3.1 setBlackMap函数

typescript 复制代码
const setBlackMap = () => {
    let { red, blue, green } = hexColorToRgba(filterColor.value)
    console.log(red, blue, green)
    if (viewer)
        modifyMap(viewer, {
            //反色?
            invertColor: true,
            brightness: 0.8,
            filterRGB: [red, green, blue],
            hue: 0.5,
            gamma: 0.2,
            contrast: 3,
            saturation: 1.5,
        });
}

该函数的主要作用是调用hexColorToRgba函数将十六进制颜色转换为RGB值,然后调用modifyMap函数修改地图样式,实现深色地图效果。

4.3.2 modifyMap函数

typescript 复制代码
const modifyMap = (viewer: Cesium.Viewer, options: any) => {
    const baseLayer = viewer.imageryLayers.get(0)
    baseLayer.brightness = options.brightness || 0.6
    baseLayer.contrast = options.contrast || 1.8
    baseLayer.gamma = options.gamma || 0.3
    baseLayer.hue = options.hue || 1
    baseLayer.saturation = options.saturation || 0
    const baseFragShader = (viewer.scene.globe as any)._surfaceShaderSet
        .baseFragmentShaderSource.sources
    for (let i = 0; i < baseFragShader.length; i++) {
        const strS = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
        let strT = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
        if (options.invertColor) {
            strT += `
                color.r = 1.0 - color.r;
                color.g = 1.0 - color.g;
                color.b = 1.0 - color.b;
                `
        }
        if (options.filterRGB.length > 0) {
            strT += `
                color.r = color.r * ${options.filterRGB[0]}.0/255.0;
                color.g = color.g * ${options.filterRGB[1]}.0/255.0;
                color.b = color.b * ${options.filterRGB[2]}.0/255.0;
                `
        }
        baseFragShader[i] = baseFragShader[i].replace(strS, strT)
    }
    nextTick(() => {
        updateViewModel()
    })
}

该函数根据传入的options对象修改地图图层的亮度、对比度、伽马值、色调、饱和度等属性,并通过修改着色器代码实现颜色反转和颜色过滤效果。最后,使用nextTick函数在DOM更新后调用updateViewModel函数更新视图模型。

4.3.3 updateViewModel函数

typescript 复制代码
const updateViewModel = () => {
    if (imageryLayers?.value?.length > 0) {
        const layer = imageryLayers.value.get(0);
        viewModel.brightness = layer.brightness;
        viewModel.contrast = layer.contrast;
        viewModel.hue = layer.hue;
        viewModel.saturation = layer.saturation;
        viewModel.gamma = layer.gamma;
    }
}

该函数用于更新viewModel中的地图属性值,确保视图模型与地图图层的属性保持一致。

4.3.4 hexColorToRgba函数

typescript 复制代码
const hexColorToRgba = (color: string) => {
    // 检查输入颜色是否以 "#" 开头
    if (!color.startsWith('#')) {
        throw new Error('Invalid hex color format. Color should start with "#".');
    }
    // 获取去掉 "#" 后的颜色值部分
    const hexValue = color.slice(1);
    // 根据颜色值长度确定是 RGB 还是 RGBA
    const isRgba = hexValue.length === 8;
    // 确保颜色值长度合法(6 或 8 位)
    if (hexValue.length !== 6 && hexValue.length !== 8) {
        throw new Error(`Invalid hex color length. Expected 6 or 8 characters, got ${hexValue.length}.`);
    }
    // 将十六进制颜色值转换为十进制整数
    const hexToInt = (hex: string) => parseInt(hex, 16);
    // 提取 RGB 分量
    const redHex = hexValue.substring(0, 2);
    const greenHex = hexValue.substring(2, 4);
    const blueHex = hexValue.substring(4, 6);
    const red = hexToInt(redHex);
    const green = hexToInt(greenHex);
    const blue = hexToInt(blueHex);
    // 如果是 RGBA,提取 Alpha 分量
    let alpha = 1;
    if (isRgba) {
        const alphaHex = hexValue.substring(6, 8);
        alpha = hexToInt(alphaHex);
    }
    return {
        red: red,
        green: green,
        blue: blue,
        alpha: alpha
    };
};

该函数用于将十六进制颜色值转换为RGB或RGBA值,同时会对输入的颜色值进行合法性检查,确保输入的颜色格式正确,这个方法对整个项目没什么影响,只是我个人习惯用16进制的颜色,这里需要获取红绿蓝各个通道的数值,所以得有一个转换的过程。

4.4 挂载阶段

typescript 复制代码
onMounted(() => {
    viewer = new Cesium.Viewer("cesiumContainer", {
        infoBox: false,
        selectionIndicator: false,
        sceneModePicker: false,
        animation: false,    //左下角的动画仪表盘
        baseLayerPicker: false,  //右上角的图层选择按钮
        geocoder: false,  //搜索框
        homeButton: false,  //home按钮
        timeline: false,    //底部的时间轴
        navigationHelpButton: false,  //右上角的帮助按钮,
        fullscreenButton: false,
        terrain: Cesium.Terrain.fromWorldTerrain(),
    });
    if (viewer.imageryLayers.length > 0)
        viewer.imageryLayers.removeAll();
    (viewer.cesiumWidget.creditContainer as HTMLElement).style.display = "none";
    let gdvMap = new Cesium.UrlTemplateImageryProvider({
        url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=2&style=8&x={x}&y={y}&z={z}',
        tileWidth: 256,
        tileHeight: 256,
        tilingScheme: new AmapMercatorTilingScheme() as any,
        maximumLevel: 18, // 根据高德地图的实际最大层级设置  
    })
    viewer.imageryLayers.addImageryProvider(gdvMap)
    setBlackMap()
});

onMounted钩子中,我们完成了以下操作:

  • 创建Cesium.Viewer实例,并对地图的一些控件(如信息框、选择指示器、场景模式选择器等)进行隐藏或移除操作,以简化地图界面。
  • 移除原有的地图图层,我们只修改图层中的第一个地图图层,所以得清空默认的图层数据后再加载自定义的数据,顺便隐藏版权信息。
  • 添加高德地图的图层gdvMap到地图中,确保只有这一层图层,然后开始修改这个涂层的样式。
  • 调用setBlackMap函数设置深色地图效果。

五、总结

以上内容就是如何使用Vue和Cesium创建一个带有自定义样式的地图视图。通过组合式API的使用,我们可以方便地管理组件的状态和生命周期,同时利用Cesium的强大功能实现地图的渲染和样式修改。这种方式为我们开发复杂的地图应用提供了一种简洁、高效的解决方案,希望本文能对大家有所帮助。

在线预览地址

相关推荐
Pedantic6 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘7 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆7 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师8 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆8 小时前
VSCode自动格式化三要素
前端
爱勇宝9 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen9 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user205855615181311 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode11 小时前
Redis 在生产项目的使用
前端·后端
LiaCode12 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端