Skip to content
Cesium绘制图形:圆形和水滴形

需求

需要实现如下图所示模块

水滴型:![[Pasted image 20241015163544.png]]

圆形:![[Pasted image 20241015163602.png]]

实现1:纯Canvas实现

圆形实际上可以通过Cesium自带的# CircleOutlineGeometry来实现,但是我这里想统一,因为水滴型是只能通过canvas绘制成img再加载进行,所以暂时统一通过canvas绘制的方法来实现

缺点

太粗暴直接加载而已,无法通过中心点和半径来改变这个园;仅仅只是为了加载出来

代码

圆形Canvas绘制代码(Rectangle就直接贴图实现)

javascript
const viewer = new Cesium.Viewer('cesiumContainer');

  

// 定义经纬度范围

const lonMin = 100.0;

const latMin = 20.0;

const lonMax = 110.0;

const latMax = 30.0;

  

const canvas = document.createElement('canvas');

canvas.width = 500;

canvas.height = 500;

const ctx = canvas.getContext('2d');

  

// 定义参数

const width = 500;

const radius = width / 2;

const lineWidth = 5; // 边框线的宽度

const strokeColor = '#3FCF8F'; // 轮廓线的颜色

  

// 清空画布

ctx.clearRect(0, 0, canvas.width, canvas.height);

  

// 开始绘制圆形轮廓

ctx.save();

ctx.beginPath();

ctx.arc(radius, radius, radius - lineWidth / 2, 0, 2 * Math.PI); // 圆形轮廓

ctx.lineWidth = lineWidth;

ctx.strokeStyle = strokeColor; // 设置边框颜色

ctx.stroke(); // 绘制边框

ctx.restore();

  

// 使用Cesium的贴地primitive加载Canvas

viewer.scene.primitives.add(

    new Cesium.GroundPrimitive({

        geometryInstances: new Cesium.GeometryInstance({

            // 和二维类似,本质上还是一个矩形

            geometry: new Cesium.RectangleGeometry({

                rectangle: Cesium.Rectangle.fromDegrees(

                    // 提供矩形的四至经纬度

                    lonMin,

                    latMin,

                    lonMax,

                    latMax

                ),

            }),

        }),

        appearance: new Cesium.Appearance({

            // 创建一个完全自定义的material

            material: new Cesium.Material({

                fabric: {

                    type: 'Image',

                    uniforms: {

                        // textureCanvas是我们创建的自定义Canvas

                        image: canvas,

                    },

                },

            }),

            flat: true,

        }),

    })

);

  

// 视角飞向贴地圆形区域

viewer.camera.flyTo({

    destination: Cesium.Rectangle.fromDegrees(lonMin, latMin, lonMax, latMax),

    duration: 3, // 飞行的时间(秒)

});

水滴形cavas绘制代码(Rectangle就直接贴图实现)

javascript
const viewer = new Cesium.Viewer('cesiumContainer');

  

// 定义经纬度范围

const lonMin = 100.0;

const latMin = 20.0;

const lonMax = 110.0;

const latMax = 30.0;

  

const canvas = document.createElement('canvas');

canvas.width = 500;

canvas.height = 500;

const ctx = canvas.getContext('2d');

  

// 定义参数

const width = 500;

const radius = width / 2;

const a = 100; // 椭圆的短轴半径

const b = 150; // 椭圆的长轴半径

const rotate = 0; // 旋转角度

const lineWidth = 5; // 轮廓线宽度

const strokeColor = '#3FCF8F'; // 轮廓线颜色

const fillColor = 'transparent'; // 填充颜色(透明)

const alpha = 1; // 透明度

const AB = radius - a; // 用于控制曲线的参数

  

// 清空画布

ctx.clearRect(0, 0, canvas.width, canvas.height);

  

// 缩小比例

ctx.scale(0.5, 1.0); // 将绘制的图形缩小一半

  

// 开始绘制水滴形轮廓

ctx.save();

ctx.beginPath();

ctx.globalAlpha = alpha;

ctx.fillStyle = fillColor || 'transparent'; // 设置填充颜色为透明

ctx.strokeStyle = strokeColor; // 设置边框颜色

ctx.lineWidth = lineWidth;

  

// 移动到水滴的起点

ctx.moveTo(radius, radius);

ctx.rotate((rotate * Math.PI) / 180);

// 绘制二次贝塞尔曲线(左边的弯曲部分)

ctx.quadraticCurveTo(radius + b, radius - AB / 2, radius + b, radius - AB);

// 绘制半椭圆(顶部圆滑部分)

ctx.ellipse(radius, radius - AB, b, a, 0, 0, Math.PI, true);

// 绘制二次贝塞尔曲线(右边的弯曲部分)

ctx.quadraticCurveTo(radius - b, radius - AB / 2, radius, radius);

  

// 绘制路径并应用样式

ctx.fill(); // 不会显示颜色,填充为空

ctx.stroke(); // 仅绘制边框

ctx.restore();

  

// 使用Cesium的贴地primitive加载Canvas

viewer.scene.primitives.add(

    new Cesium.GroundPrimitive({

        geometryInstances: new Cesium.GeometryInstance({

            // 和二维类似,本质上还是一个矩形

            geometry: new Cesium.RectangleGeometry({

                rectangle: Cesium.Rectangle.fromDegrees(

                    // 提供矩形的四至经纬度

                    lonMin,

                    latMin,

                    lonMax,

                    latMax

                ),

            }),

        }),

        appearance: new Cesium.Appearance({

            // 创建一个完全自定义的material

            material: new Cesium.Material({

                fabric: {

                    type: 'Image',

                    uniforms: {

                        // textureCanvas是我们创建的自定义Canvas

                        image: canvas,

                    },

                },

            }),

            flat: true,

        }),

    })

);

  

// 视角飞向贴地圆形区域

viewer.camera.flyTo({

    destination: Cesium.Rectangle.fromDegrees(lonMin, latMin, lonMax, latMax),

    duration: 3, // 飞行的时间(秒)

});

实现2:Canvas + CircleGeometry实现

圆形轮廓线贴地(传中心点+半径)

javascript
const viewer = new Cesium.Viewer('cesiumContainer');

// 定义中心点
const centerPoint = {
    longitude: 112.0285,
    latitude: 37.43843,
};

const radiusInMeters = 50; // 半径设置为50米

// 创建 canvas 并绘制圆形图案
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 500;
const ctx = canvas.getContext('2d');

// 定义圆形参数
const width = 500;
const radius = width / 2;
const lineWidth = 5; // 边框线宽
const strokeColor = '#3FCF8F'; // 边框颜色

// 清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);

// 绘制圆形
ctx.save();
ctx.beginPath();
ctx.arc(radius, radius, radius - lineWidth / 2, 0, 2 * Math.PI); // 圆形轮廓
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeColor; // 设置边框颜色
ctx.stroke(); // 绘制边框
ctx.restore();

// 使用 CircleGeometry 来绘制贴地的圆形
viewer.scene.primitives.add(
    new Cesium.GroundPrimitive({
        geometryInstances: new Cesium.GeometryInstance({
            geometry: new Cesium.CircleGeometry({
                center: Cesium.Cartesian3.fromDegrees(centerPoint.longitude, centerPoint.latitude),
                radius: radiusInMeters, // 半径设置为50米
            }),
        }),
        appearance: new Cesium.Appearance({
            material: new Cesium.Material({
                fabric: {
                    type: 'Image',
                    uniforms: {
                        image: canvas, // 自定义的canvas图形作为材质
                    },
                },
            }),
            flat: true,
        }),
    })
);

// 添加中心点标记
viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(centerPoint.longitude, centerPoint.latitude),
    point: {
        pixelSize: 10, // 点的大小
        color: Cesium.Color.RED, // 点的颜色
        outlineColor: Cesium.Color.WHITE, // 外轮廓的颜色
        outlineWidth: 2, // 外轮廓的宽度
    },
});

// 飞向圆形区域
viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(centerPoint.longitude, centerPoint.latitude, 500), // 设置飞行高度
});

为什么不直接用CircleOutlineGeometry来绘制

因为CircleOutlineGeometry不支持贴地,如果要做的话,要用turfJS来把圆半径外的每一个点算出来,再用线贴地的方式来实现(现阶段太麻烦,直接用这种先),先加个todo吧

问题

1、如何确保我给的中心点就是圆的中心点 / 水滴的下方中心点

2、 要加标签怎么加?

Updated at: