无人机管理后台前端(已迁走)
shuishen
2025-06-11 dae8d5ad8df88593261f6df3697fbfe2cab3047b
feat:后台管理通用航线加载相关方法、hook、航线材质等处理
1 files modified
3 files added
865 ■■■■■ changed files
src/hooks/common.js 172 ●●●●● patch | view | raw | blame | history
src/hooks/useRouteLine/useRouteLine.js 225 ●●●●● patch | view | raw | blame | history
src/utils/cesium/Material/index.js 442 ●●●●● patch | view | raw | blame | history
src/utils/util.js 26 ●●●● patch | view | raw | blame | history
src/hooks/common.js
New file
@@ -0,0 +1,172 @@
import * as Cesium from 'cesium'
import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
import { camelToSnake } from '@/utils/util'
import _, { cloneDeep, throttle } from 'lodash'
import * as turf from '@turf/turf'
/**
 * 判断action里面是否有key
 * @param action
 * @param key
 * @returns {{hasKey: boolean, val: *}|{hasKey: boolean, val: null}}
 */
export function actionHasKey (action, key) {
    const curActionParams = action?.action_actuator_func_param
    const val = curActionParams?.[key]
    if (val !== undefined) {
        return { hasKey: true, val }
    } else {
        return { hasKey: false, val: null }
    }
}
// 解析kmz文件的时候处理pointList 写在这里
export function handlePointListForKmz ({ placemark, startPoint, execute_height_mode, auto_flight_speed }) {
    if (!placemark?.length) return []
    const isWGS84 = execute_height_mode === 'WGS84'
    const list = placemark.map(item => {
        const [longitude, latitude] = item.point.coordinates.trim().split(',')
        let action_modes = item?.action_group?.action || []
        action_modes = Array.isArray(action_modes) ? action_modes : [action_modes]
        return {
            longitude: _.round(longitude, 8),
            latitude: _.round(latitude, 8),
            height: isWGS84 ? item.execute_height : item.execute_height + startPoint.height,
            waypoint_speed: item.waypoint_speed,
            action_modes,
        }
    })
    const pointStart = {
        ...startPoint,
        flyRotateYaw: 0,
        pointType: 'start',
        waypoint_speed: auto_flight_speed,
        heading: 0, // 椎体 heading
        arrowHeading: 0, // 飞行器 heading
        pitch: 0, //椎体俯仰角
        fov: 19.5, // 这个是椎体的展示截面大小
    }
    list.forEach(item => {
        item?.action_modes?.forEach(item1 => {
            let { hasKey: has_focal_length, val: focal_length } = actionHasKey(item1, 'focal_length')
            // 处理focalLength需要/24  focalLength可能是‘1,024’
            if (has_focal_length && focal_length) {
                focal_length = typeof focal_length === 'string' ? focal_length.replace(/,/g, '') : focal_length
                item1.action_actuator_func_param.focal_length = Number(focal_length) / 24
            }
        })
        item.arrowHeading = 0
    })
    return [pointStart, ...list]
}
export async function analysisPointLineKmz (kmzUrl) {
    const res = await analyzeKmzFile(`${kmzUrl}?_t=${new Date().getTime()}`)
    const templateXML = await res.fileInfoObj['wpmz/template.kml']
    const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
    const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
    const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document']
    const templateXMLObj = camelToSnake(removeTextKey(templateXMLJSON))
    const waylinesXMLObj = camelToSnake(removeTextKey(waylinesXMLJSON))
    const {
        mission_config: {
            take_off_ref_point,
            drone_info: { drone_enum_value, drone_sub_enum_value } = {}
        } = {},
        folder: {
            payload_param: { image_format } = {},
            placemark: {
                polygon
            },
            global_height, auto_flight_speed,
            template_type: templateType
        } = {},
    } = templateXMLObj
    const {
        mission_config: { take_off_security_height } = {},
        folder: polygonPointFolder
    } = waylinesXMLObj
    let execute_height_mode = '', pointPlacemark
    if (templateType === "mapping3d") {
        execute_height_mode = polygonPointFolder[0].execute_height_mode
        pointPlacemark = polygonPointFolder.map(i => i.placemark)
    } else {
        execute_height_mode = polygonPointFolder.execute_height_mode
        pointPlacemark = polygonPointFolder.placemark
    }
    const [latitude, longitude, height] = take_off_ref_point.split(',').map(item => _.round(item, 6))
    let startPoint = { latitude, longitude, height }
    // 取出点位
    const coordinates = polygon?.outer_boundary_is.linear_ring
        .coordinates?.split('\n') || []
    // 数组转换
    let polygonList = coordinates.map((coordinate) =>
        coordinate
            .replace(/\s+/g, '')
            .split(',')
            .map((v) => Number(v)),
    )
    polygonList.pop()
    return {
        image_format,
        startPoint,
        global_height,
        auto_flight_speed,
        take_off_ref_point,
        drone_enum_value,
        drone_sub_enum_value,
        take_off_security_height,
        execute_height_mode,
        templateType,
        pointPlacemark,
        polygonList
    }
}
// 获取连接地面线
export function getPolyLine (viewer, pointList, index) {
    return {
        positions: new Cesium.CallbackProperty(() => {
            const point = pointList.value[index]
            const pointPosition = Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height)
            if (!pointPosition) return []
            // 获取点的位置
            const cartographic = Cesium.Cartographic.fromCartesian(pointPosition)
            // 获取地形高度
            const terrainHeight = viewer.scene.globe.getHeight(cartographic) || 0
            // 创建地面点位置
            const groundPosition = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, terrainHeight)
            return [pointPosition, groundPosition]
        }, false),
        width: 0.5,
        material: Cesium.Color.WHITE,
    }
}
export function getNewPolygonData (data) {
    const polygonData = data.reduce((pre, cur) => {
        let arr = cur.data.map(i => {
            if ('point' in i) return i.point.coordinates.split(',')
            if ('Point' in i) {
                if (_.isObject(i.Point.coordinates) && '#text' in i.Point.coordinates) return i.Point.coordinates['#text'].split(',')
                return i.Point.coordinates.split(',')
            }
        }).map(i => turf.point([Number(i[0]), Number(i[1])]))
        return pre.push(...arr), pre
    }, [])
    const hull = turf.convex(turf.featureCollection(polygonData))
    return hull.geometry.coordinates[0]
}
src/hooks/useRouteLine/useRouteLine.js
New file
@@ -0,0 +1,225 @@
import * as Cesium from 'cesium'
import {
    analysisPointLineKmz,
    handlePointListForKmz,
    getPolyLine,
    getNewPolygonData,
} from '@/hooks/common'
import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png'
import endPointImg from '@/assets/images/EndPointicon.png'
import { ArrowLineMaterialProperty } from '@/utils/cesium/Material'
import { flyVisual } from '@/utils/cesium/mapUtil'
let arrowLineMaterialProperty = new ArrowLineMaterialProperty({
    color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1),
    directionColor: new Cesium.Color(1, 1, 1, 1),
    outlineColor: new Cesium.Color(1, 1, 1, 1),
    outlineWidth: 0,
    speed: 5,
})
export function useRouteLine () {
    let viewer = null
    let previewDataSource = null
    const curRouteLineData = ref({
        data: [],
        polygonList: '',
        templateType: '',
        startPoint: '',
        execute_height_mode: '',
        auto_flight_speed: '',
        wayline_type: '',
    })
    const routeLineListClick = async data => {
        if (data.select) return
        curRouteLineData.value.data.forEach(i => (i.select = false))
        data.select = true
        await renderRouteLine(data.data)
    }
    async function renderPreviewLine (kmzUrl, wayline_type, cb = () => { }) {
        resetCurRouteLineData()
        const { pointPlacemark, polygonList, templateType, startPoint, execute_height_mode, auto_flight_speed } =
            await analysisPointLineKmz(kmzUrl)
        curRouteLineData.value = {
            polygonList,
            templateType,
            startPoint,
            execute_height_mode,
            auto_flight_speed,
            wayline_type,
        }
        cb(curRouteLineData.value.polygonList.map(item => [Number(item[0]), Number(item[1])]))
        if (templateType === 'mapping3d') {
            curRouteLineData.value.data = pointPlacemark.map(item => ({
                data: item,
                select: false,
            }))
            routeLineListClick(curRouteLineData.value.data[0])
        } else {
            curRouteLineData.value.data = []
            await renderRouteLine(pointPlacemark)
        }
    }
    async function renderRouteLine (data) {
        if (previewDataSource) {
            removePreviewLine()
        } else {
            previewDataSource = new Cesium.CustomDataSource('previewDataSource')
            await viewer.dataSources.add(previewDataSource)
        }
        let lineMaterial = [2, 4].includes(Number(curRouteLineData.value.wayline_type))
            ? Cesium.Color.fromCssColorString('#00D690')
            : arrowLineMaterialProperty
        if ([2, 4].includes(Number(curRouteLineData.value.wayline_type))) {
            if (curRouteLineData.value.templateType === 'mapping3d') {
                let newPolygonData = getNewPolygonData(curRouteLineData.value.data)
                let newPolygonDisposePositions = newPolygonData.map(i =>
                    Cesium.Cartesian3.fromDegrees(Number(i[0]), Number(i[1]))
                )
                previewDataSource.entities.add({
                    polygon: {
                        hierarchy: new Cesium.CallbackProperty(() => {
                            return new Cesium.PolygonHierarchy(newPolygonDisposePositions)
                        }, false),
                        material: Cesium.Color.fromBytes(255, 228, 22, 44),
                        outline: false,
                        outlineWidth: 2,
                        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                    },
                })
            }
            let polygonDisposePositions = curRouteLineData.value.polygonList.map(item => {
                return Cesium.Cartesian3.fromDegrees(Number(item[0]), Number(item[1]), Number(item[2]))
            })
            previewDataSource.entities.add({
                polygon: {
                    hierarchy: new Cesium.CallbackProperty(() => {
                        return new Cesium.PolygonHierarchy(polygonDisposePositions)
                    }, false),
                    material: Cesium.Color.fromBytes(45, 140, 240, 99),
                    outline: false,
                    outlineWidth: 2,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                },
                polyline: {
                    width: 2,
                    material: Cesium.Color.fromBytes(45, 140, 240, 255),
                    clampToGround: true,
                    positions: new Cesium.CallbackProperty(() => {
                        return [...polygonDisposePositions, polygonDisposePositions[0]]
                    }, false),
                },
            })
        }
        let pointList = handlePointListForKmz({
            placemark: data,
            startPoint: curRouteLineData.value.startPoint,
            execute_height_mode: curRouteLineData.value.execute_height_mode,
            auto_flight_speed: curRouteLineData.value.auto_flight_speed,
        })
        pointList.shift()
        const routePositions = pointList.map(i => Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(i.height)))
        previewDataSource.entities.add({
            polyline: {
                width: 4,
                positions: routePositions,
                material: lineMaterial,
                clampToGround: false,
            },
        })
            // 落点线
            ; (pointList || [])?.forEach((item, index) => {
                const topPosition = Cesium.Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude), Number(item.height))
                let setting = {
                    position: topPosition,
                    polyline: getPolyLine(viewer, { value: pointList }, index),
                }
                previewDataSource.entities.add(setting)
            })
        // 终点
        previewDataSource.entities.add({
            position: routePositions[routePositions.length - 1],
            billboard: {
                image: new Cesium.ConstantProperty(endPointImg),
                width: 30,
                height: 30,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
            },
        })
        // 起点
        previewDataSource.entities.add({
            position: routePositions[0],
            billboard: {
                image: rwqfdImg,
                width: 50,
                height: 50,
            },
        })
        flyVisual(
            pointList.map(i => [Number(i.longitude), Number(i.latitude), Number(i.height)]),
            viewer,
            20
        )
    }
    function removePreviewLine () {
        previewDataSource?.entities.removeAll()
    }
    function resetCurRouteLineData () {
        curRouteLineData.value = {
            data: [],
            polygonList: '',
            templateType: '',
            startPoint: '',
            execute_height_mode: '',
            auto_flight_speed: '',
            wayline_type: '',
        }
    }
    const initViewer = (v) => {
        viewer = v
    }
    onBeforeUnmount(() => {
        resetCurRouteLineData()
        removePreviewLine()
    })
    return {
        curRouteLineData,
        routeLineListClick,
        initViewer,
        renderPreviewLine,
        removePreviewLine,
        resetCurRouteLineData
    }
}
src/utils/cesium/Material/index.js
New file
@@ -0,0 +1,442 @@
import * as Cesium from 'cesium'
// 多边形渐变材质
let pM = "PolyGradientMaterial"
Cesium.Material._materialCache.addMaterial(pM, {
  strict: true,
  fabric: {
    type: pM,
    uniforms: {
      color: new Cesium.Color(1.0, 1.0, 0.0, 0.5),
      diffusePower: 1.6,
      alphaPower: 1.5,
      center: new Cesium.Cartesian2(0.5, 0.5),
      isInner: false,
      globalAlpha: 1.0,
    },
    source: `
            uniform vec4 color;
            uniform float diffusePower;
            uniform float alphaPower;
            uniform float globalAlpha;
            uniform vec2 center;
            uniform bool isInner;
            czm_material czm_getMaterial(czm_materialInput materialInput) {
                czm_material material = czm_getDefaultMaterial(materialInput);
                vec2 st = materialInput.st;
                float alphaMars3D = distance(st, center);
                if(isInner) {
                    material.alpha = (1.0 - (color.a * alphaMars3D * alphaPower)) * globalAlpha;
                    if(material.alpha < 0.0) material.alpha = 0.0;
                } else {
                    material.alpha = color.a * alphaMars3D * alphaPower * globalAlpha;
                }
                material.diffuse = color.rgb * diffusePower;
                return material;
            }
        `,
  },
  translucent: true,
})
class MaterialProperty {
  constructor(options = {}) {
    this._definitionChanged = new Cesium.Event()
    this._color = undefined
    this._glowPower = undefined
    this._taperPower = undefined
    this._directionColor = undefined
    this._outlineColor = undefined
    this._outlineWidth = undefined
    this._opacity = undefined
    this._alphaPower = undefined
    this._speed = undefined
    this.color = options.color || Cesium.Color.fromBytes(0, 255, 255, 255)
    this.glowPower = .25
    this.taperPower = 1
    this.directionColor = options.directionColor || Cesium.Color.fromBytes(255, 255, 255, 255)
    this.outlineColor = options.outlineColor || Cesium.Color.fromBytes(255, 255, 255, 255)
    this.outlineWidth = 0
    this.opacity = options.opacity || 0.5
    this.alphaPower = options.alphaPower || 1.5
    this.speed = options.speed || 5
  }
  get isConstant () {
    return false
  }
  get definitionChanged () {
    return this._definitionChanged
  }
  getType (time) {
    return null
  }
  getValue (time, result) {
    result = Cesium.defaultValue(result, {})
    return result
  }
  equals (other) {
    return this === other
  }
}
class PolyGradientMaterial extends MaterialProperty {
  constructor(options = {}) {
    super(options)
  }
  getType (time) {
    return pM
  }
  getValue (time, result) {
    if (!result) {
      result = {}
    }
    result.color = Cesium.Property.getValueOrUndefined(this._color, time)
    result.globalAlpha = Cesium.Property.getValueOrUndefined(this._opacity, time)
    result.alphaPower = Cesium.Property.getValueOrUndefined(this._alphaPower, time)
    return result
  }
  equals (other) {
    return (
      this === other ||
      (other instanceof PolyGradientMaterial &&
        Cesium.Property.equals(this._color, other._color) &&
        Cesium.Property.equals(this._opacity, other._opacity) &&
        Cesium.Property.equals(this._alphaPower, other._alphaPower))
    )
  }
}
Object.defineProperties(PolyGradientMaterial.prototype, {
  color: Cesium.createPropertyDescriptor('color'),
  opacity: Cesium.createPropertyDescriptor('opacity'),
  alphaPower: Cesium.createPropertyDescriptor('alphaPower'),
})
// 轨迹线材质
let lT = "LineTrailMaterial"
Cesium.Material._materialCache.addMaterial(lT, {
  fabric: {
    uniforms: {
      color: new Cesium.Color(1.0, 1.0, 0.0, 0.7),
      bgColor: new Cesium.Color(0.0, 1.0, 0.0, 0.0),
      speed: 5,
      globalAlpha: 1.0
    },
    source: `
          uniform vec4 bgColor;
          uniform vec4 color;
          uniform float speed;
          uniform float globalAlpha;
          czm_material czm_getMaterial(czm_materialInput materialInput) {
              czm_material material = czm_getDefaultMaterial(materialInput);
              vec2 st = materialInput.st;
              float time = fract(czm_frameNumber * speed / 1000.0);
              vec3 colorMars3D = color.rgb;
              if (st.t > 0.45 && st.t < 0.55) {
                  colorMars3D = vec3(1.0);
              }
              material.alpha = color.a * 1.5 * smoothstep(0.0, 1.0, fract(st.s - time));
              material.diffuse = max(colorMars3D.rgb * material.alpha, colorMars3D.rgb);
              if (material.alpha < bgColor.a) {
                  material.alpha = bgColor.a;
                  material.diffuse = bgColor.rgb;
              }
              material.alpha = material.alpha * globalAlpha;
              return material;
          }
      `
  },
  translucent: true
})
class LineTrailMaterial extends MaterialProperty {
  constructor(options = {}) {
    super(options)
  }
  getType (time) {
    return lT
  }
  getValue (time, result) {
    if (!result) {
      result = {}
    }
    result.color = Cesium.Property.getValueOrUndefined(this._color, time)
    result.globalAlpha = Cesium.Property.getValueOrUndefined(this._opacity, time)
    result.speed = Cesium.Property.getValueOrUndefined(this._speed, time)
    return result
  }
  equals (other) {
    return (
      this === other ||
      (other instanceof LineTrailMaterial &&
        Cesium.Property.equals(this._color, other._color) &&
        Cesium.Property.equals(this._opacity, other._opacity) &&
        Cesium.Property.equals(this._speed, other._speed))
    )
  }
}
Object.defineProperties(LineTrailMaterial.prototype, {
  color: Cesium.createPropertyDescriptor('color'),
  opacity: Cesium.createPropertyDescriptor('opacity'),
  speed: Cesium.createPropertyDescriptor('speed'),
})
let arrowLineMaterialType = "ArrowLineMaterial"
Cesium.Material._materialCache.addMaterial(arrowLineMaterialType, {
  fabric: {
    type: arrowLineMaterialType,
    uniforms: {
      color: new Cesium.Color(0, 1, 1, 1),
      directionColor: new Cesium.Color(1, 1, 1, 1),
      outlineColor: new Cesium.Color(1, 1, 1, 1),
      outlineWidth: 0,
      speed: 0,  // Added speed uniform with default 0 (no animation)
    },
    source: `
#ifdef GL_OES_standard_derivatives
#extension GL_OES_standard_derivatives : enable
#endif
uniform vec4 color;
uniform vec4 directionColor;
uniform vec4 outlineColor;
uniform float outlineWidth;
uniform float speed;      // Added speed uniform
in float v_width;
in float v_polylineAngle;
const float fragLength = 100.0;
const float startPosition = 0.45;
const float endPosition = 0.55;
mat2 rotate(float rad) {
    float c = cos(rad);
    float s = sin(rad);
    return mat2(c, s, -s, c);
}
float getPointOnLine(vec2 p0, vec2 p1, float x) {
    float slope = (p0.y - p1.y) / (p0.x - p1.x);
    return slope * (x - p0.x) + p0.y;
}
czm_material czm_getMaterial(czm_materialInput materialInput) {
    czm_material material = czm_getDefaultMaterial(materialInput);
    vec2 st = materialInput.st;
    float time = fract(czm_frameNumber * speed / 1000.0);
    float halfInteriorWidth = 0.5 * (v_width - outlineWidth) / v_width;
    float b = step(0.5 - halfInteriorWidth, st.t);
    b *= 1.0 - step(0.5 + halfInteriorWidth, st.t);
    float d1 = abs(st.t - (0.5 - halfInteriorWidth));
    float d2 = abs(st.t - (0.5 + halfInteriorWidth));
    float dist = min(d1, d2);
    vec4 currentColor = mix(outlineColor, color, b);
    vec4 outColor = czm_antialias(outlineColor, color, currentColor, dist);
    outColor = czm_gammaCorrect(outColor);
    vec2 pos = rotate(v_polylineAngle) * gl_FragCoord.xy;
    // Modified to include animation based on speed and time
    float animationOffset = speed * time;
    float maskS = fract((pos.x / (fragLength * czm_pixelRatio)) - animationOffset);
    float maskT = st.t;
    bool isDirection = (maskS > startPosition) && (maskS <= endPosition);
    vec4 fragColor;
    if (isDirection) {
        float arrowWidth = (endPosition - startPosition) / 2.0;
        float midS = startPosition + arrowWidth;
        float t = 1.0;
        if (maskS < midS) {
            vec2 center = vec2(midS, 0.5);
            float ptOnUpperLine = getPointOnLine(vec2(startPosition, 1.0), center, maskS);
            float ptOnLowerLine = getPointOnLine(vec2(startPosition, 0.0), center, maskS);
            t *= 1.0 - step(ptOnUpperLine, maskT);
            t *= step(ptOnLowerLine, maskT);
            t = 1.0 - t;
        } else {
            vec2 center = vec2(endPosition, 0.5);
            float ptOnUpperLine = getPointOnLine(vec2(midS, 1.0), center, maskS);
            float ptOnLowerLine = getPointOnLine(vec2(midS, 0.0), center, maskS);
            t *= 1.0 - step(ptOnUpperLine, maskT);
            t *= step(ptOnLowerLine, maskT);
        }
        vec4 outsideColor = outColor;
        vec4 currentColor = mix(outsideColor, directionColor, clamp(t, 0.0, 1.0));
        fragColor = currentColor;
    } else {
        fragColor = outColor;
    }
    fragColor = czm_gammaCorrect(fragColor);
    material.diffuse = fragColor.rgb;
    material.alpha = fragColor.a;
    return material;
}`
  },
  translucent: true
})
class ArrowLineMaterialProperty extends MaterialProperty {
  constructor(options = {}) {
    super(options)
  }
  getType (time) {
    return arrowLineMaterialType
  }
  getType (time) {
    return arrowLineMaterialType
  }
  getValue (time, result) {
    if (!result) {
      result = {}
    }
    result.color = Cesium.Property.getValueOrUndefined(this._color, time)
    result.directionColor = Cesium.Property.getValueOrUndefined(this._directionColor, time)
    result.outlineColor = Cesium.Property.getValueOrUndefined(this._outlineColor, time)
    result.outlineWidth = Cesium.Property.getValueOrUndefined(this._outlineWidth, time)
    result.speed = Cesium.Property.getValueOrUndefined(this._speed, time)
    return result
  }
  equals (other) {
    return (
      this === other ||
      (other instanceof ArrowLineMaterialProperty &&
        Cesium.Property.equals(this._color, other._color) &&
        Cesium.Property.equals(this._directionColor, other._directionColor) &&
        Cesium.Property.equals(this._outlineColor, other._outlineColor) &&
        Cesium.Property.equals(this._outlineWidth, other._outlineWidth) &&
        Cesium.Property.equals(this._speed, other._speed))
    )
  }
}
Object.defineProperties(ArrowLineMaterialProperty.prototype, {
  color: Cesium.createPropertyDescriptor('color'),
  directionColor: Cesium.createPropertyDescriptor('directionColor'),
  outlineColor: Cesium.createPropertyDescriptor('outlineColor'),
  outlineWidth: Cesium.createPropertyDescriptor('outlineWidth'),
  speed: Cesium.createPropertyDescriptor('speed')
})
// 轨迹线材质
let pLG = "PolylineGlow"
Cesium.Material._materialCache.addMaterial(pLG, {
  fabric: {
    uniforms: {
      color: new Cesium.Color(0, .5, 1, 1),
      glowPower: .25,
      taperPower: 1
    },
    source: ` uniform vec4 color;
              uniform float glowPower;
              uniform float taperPower;
              czm_material czm_getMaterial(czm_materialInput materialInput)
              {
                  czm_material material = czm_getDefaultMaterial(materialInput);
                  vec2 st = materialInput.st;
                  float glow = glowPower / abs(st.t - 0.5) - (glowPower / 0.5);
                  if (taperPower <= 0.99999) {
                      glow *= min(1.0, taperPower / (0.5 - st.s * 0.5) - (taperPower / 0.5));
                  }
                  vec4 fragColor;
                  fragColor.rgb = max(vec3(glow - 1.0 + color.rgb), color.rgb);
                  fragColor.a = clamp(0.0, 1.0, glow) * color.a;
                  fragColor = czm_gammaCorrect(fragColor);
                  material.emission = fragColor.rgb;
                  material.alpha = fragColor.a;
                  return material;
              }
              `
  },
  translucent: true
})
class PolylineGlowMaterial extends MaterialProperty {
  constructor(options = {}) {
    super(options)
  }
  getType (time) {
    return pLG
  }
  getValue (time, result) {
    if (!result) {
      result = {}
    }
    result.color = Cesium.Property.getValueOrUndefined(this._color, time)
    result.glowPower = Cesium.Property.getValueOrUndefined(this._glowPower, time)
    result.taperPower = Cesium.Property.getValueOrUndefined(this._taperPower, time)
    return result
  }
  equals (other) {
    return (
      this === other ||
      (other instanceof LineTrailMaterial &&
        Cesium.Property.equals(this._color, other._color) &&
        Cesium.Property.equals(this._glowPower, other._glowPower) &&
        Cesium.Property.equals(this._taperPower, other._taperPower))
    )
  }
}
Object.defineProperties(PolylineGlowMaterial.prototype, {
  color: Cesium.createPropertyDescriptor('color'),
  glowPower: Cesium.createPropertyDescriptor('glowPower'),
  taperPower: Cesium.createPropertyDescriptor('taperPower'),
})
export {
  PolyGradientMaterial,
  LineTrailMaterial,
  ArrowLineMaterialProperty,
  PolylineGlowMaterial
}
src/utils/util.js
@@ -125,9 +125,9 @@
 * @returns {string}
 */
export const getSmallImg = url => {
    if (!url) return ''
    const lastDotIndex = url.lastIndexOf('.')
    return `${url.substring(0, lastDotIndex)}_small${url.substring(lastDotIndex)}`
  if (!url) return ''
  const lastDotIndex = url.lastIndexOf('.')
  return `${url.substring(0, lastDotIndex)}_small${url.substring(lastDotIndex)}`
}
/**
 * 浏览器判断是否全屏
@@ -438,3 +438,23 @@
  // 设置默认范围 [上个月, 当前月]
  return [new Date(lastMonthYear, lastMonth, 1), new Date(currentYear, currentMonth, 1)]
}
// key驼峰转换为下划线
export function camelToSnake (obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj
  }
  if (Array.isArray(obj)) {
    return obj.map(item => camelToSnake(item))
  }
  const newObj = {}
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      const newKey = key.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase()
      newObj[newKey] = camelToSnake(obj[key])
    }
  }
  return newObj
}