Skip to content

数据加载

需要启用模型平移插件

通用代码

获取加载参数

通过数据的 id 进行数据加载。数据的 id 可从数据列表中获取。

ts
// 通过数据的 id 进行数据加载,获取上传参数
const { data } = await getDataInfo({ id: '89ae976b54517804316a83e79dbcada0' })
const { bizType, fileType, id, jsonPath, position, name } =
  getPreviewParams(data)
const res = await getServiceAttr({ folderId: id })
const attrData = res.data?.[0]
const attr = getAttrJson(attrData) //素材属性对象,无属性时为空对象
const coverImg = attrData?.coverImg //缩略图路径

/**
 * 获取数据详情
 * @param params 数据id
 */
function getDataInfo(params: { id: string }) {
  return request({
    url: '/admin/tx/data/info',
    method: 'get',
    params
  })
}
/**
 * 获取加载参数
 * @param data 来自数据列表
 */
function getPreviewParams(data: any) {
  return {
    bizType: data.bizType,
    jsonPath: `/api/admin/tx/preview/data/index/${data.id}?token=${CONSTANTS.TOKEN}`,
    fileType: data.fileType,
    id: data.id,
    position: data.position,
    name: data.name
  }
}
/**获取素材属性-用于保存位置、样式 */
function getServiceAttr(params: { folderId: string }) {
  return request({
    url: '/admin/tx/service/attr/get',
    method: 'get',
    params
  })
}
/**获取素材属性JSON */
function getAttrJson<T = Record<any, any>>(dataObj?: Record<string, any>) {
  if (!dataObj) return {} as T
  const attr = (() => {
    try {
      return JSON.parse(decodeURIComponent(dataObj?.attrJson))
    } catch (error) {
      return {} as T
    }
  })()
  return {
    ...attr
  } as T
}
/**保存/更新素材属性 */
function saveServiceAttr(data: {
  attrJson?: string //属性json
  coverImg?: string //缩略图路径
  folderId: string //数据id
  id?: string //属性对象的id
}) {
  return request({
    url: '/admin/tx/service/attr/save',
    method: 'post',
    data
  })
}
/**上传图片
 * @param data.folderId //传递文件的id
 * @param data.imageBase64Code//base64编码,必须为jpeg格式
 */
function serviceAttrUploadTempImg(data: {
  folderId: string
  imageBase64Code: string
}) {
  return request({
    url: '/admin/tx/service/attr/uploadTempImg',
    method: 'post',
    data
  })
}

保存素材属性通用

保存素材属性,这里保存的属性会在场景初始加载时使用。

ts
/**
 * 保存素材属性
 * @param attrJson 素材属性对象,不同的素材类型属性不同,见下文各个素材类型的属性类型
 */
async function saveAttr<T>(attrJson: T) {
  const coverImg = await serviceAttrUploadTempImg({
    folderId: id,
    imageBase64Code: 'data:image/jpeg;base64,someBase64Code' //base64编码,必须为jpeg格式
  })
  //获取之前保存的样式Id,如果没有则为undefined
  const attrId = await (async () => {
    const getAttrRes = await getServiceAttr({
      folderId: formModel.id ?? ''
    })
    return getAttrRes.data?.[0]?.id
  })()
  //id为空时为新增,否则为更新
  await saveServiceAttr({
    folderId: formModel.id ?? '',
    id: attrId,
    coverImg: cover_img,
    attrJson: encodeURIComponent(JSON.stringify(attrJson))
  })
}

影像

bizType
1

影像属性类型

ts
type imageryAttributes = {
  attachType: number //1:依模型  0:依地表
  opacity: number //透明度 0-100; 百分比
}

加载影像

加载影像,返回影像图层对象进行后续操作

加载影像需要的参数

获取加载参数

ts
/**
 * 加载影像
 * @param id 数据id
 * @param jsonPath 影像json路径
 * @param subdomains 子域(不必填)
 */
async function loadImagery(
  {
    id,
    jsonPath,
    subdomains
  }: {
    id: string
    jsonPath: string
    subdomains?: string[]
  } = {
    subdomains: []
  }
) {
  const { imageryLayers } = viewer
  const params: any = {
    url: jsonPath
  }
  if (subdomains.length > 0) {
    params.baseUri = 'http://{s}/' + id + '/{z}/{x}/{y}.png'
    params.subdomains = subdomains
  }
  try {
    // @ts-ignore
    const lsImageryProvider = await LSGlobe.LSImageryProvider.fromUrl(
      params.url,
      params
    )
    const imageryLayer = imageryLayers.addImageryProvider(lsImageryProvider, 3)
    const flyFn = () => viewer!.flyTo(imageryLayer)
    flyFn()
    return { imageryLayer }
  } catch (error) {
    console.error(`Error loading imagery: ${error}`)
  }
  return {
    imageryLayer: null
  }
}

加载样式

加载样式使用类型

影像属性类型

ts
init_imagery_style(imageryLayer, attr)
function init_imagery_style<T extends imageryAttributes>(
  imageryLayer: any,
  attr: T
) {
  imageryLayer.alpha = (attr.opacity ?? 100) / 100
}

保存属性

保存属性使用方法/类型

保存素材属性通用

影像属性类型

ts
saveAttr<imageryAttributes>({
  attachType: 1,
  opacity: 100
})

地形

bizType
2

地形属性类型

加载地形

加载地形需要的参数

获取加载参数

ts
/**
 * 加载地形
 * @param id 数据id
 * @param jsonPath 地形json路径
 * @param subdomains 子域(不必填)
 */
async function loadTerrain(
  {
    id,
    jsonPath,
    subdomains
  }: {
    id: string
    jsonPath: string
    subdomains?: string[]
  } = {
    subdomains: []
  }
) {
  // @ts-ignore
  const { terrainLayers } = viewer
  const params: any = {
    url: jsonPath
  }
  if (subdomains.length > 0) {
    params.baseUri = 'http://{s}/' + id + '/{z}/{x}/{y}.png'
    params.subdomains = subdomains
  }
  try {
    const lsTerrainProvider = await LSGlobe.LSTerrainProvider.fromUrl(
      jsonPath,
      params
    )
    terrainLayers.addTerrainProvider(lsTerrainProvider)
    const flyFn = () => {
      viewer!.camera.flyTo({
        destination: lsTerrainProvider._rectangle
      })
    }
    flyFn()
  } catch (error) {
    console.error(`Error loading Terrain: ${error}`)
  }
}

加载样式

保存属性

保存属性使用方法/类型

保存素材属性通用

ts
saveAttr({})

倾斜模型

bizType
3

倾斜模型属性类型

ts
type obliqueModelAttributes = {
  lon: number // 经度
  lat: number // 纬度
  height: number // 海拔高度
  opacity: number // 不透明度0-100 百分比
  granularity: number // 精细度0-16
  modelMatrix: number[] //模型矩阵
}

加载倾斜模型

通用的加载函数,bizType 为 3 /4/ 5 时调用此函数

加载倾斜模型需要的参数

获取加载参数

ts
/**
 * 加载倾斜模型
 * @param id 数据id
 * @param jsonPath 倾斜模型json路径
 * @param bizType 数据bizType
 */
async function loadArtificialModel({ id, position, jsonPath, bizType }) {
  try {
    const tileset = await (LSGlobe.Cesium3DTileset as any).fromUrl(jsonPath, {
      skipLevelOfDetail: true
    })
    viewer!.scene.primitives.add(tileset)
    if (5 == bizType) {
      processTileset(tileset)
    } else if (3 == bizType) {
      tileset.cacheBytes = 1024 * 1024 * 1024 * 1
    }
    const addpointStrs = position
      ? position.split(',')?.map((e) => Number(e))
      : getTileSetPosition(tileset)
    //等待200ms
    await (() => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve()
        }, 200)
      })
    })()
    const flyFn = () => {
      viewer!.camera.flyToBoundingSphere(tileset.boundingSphere)
      viewer!.camera.lookAtTransform(LSGlobe.Matrix4.IDENTITY)
    }
    flyFn()
    return { tileset, tilesetPosition: addpointStrs }
  } catch (error) {
    console.error(`Error loading tileset: ${error}`)
  }
  return {
    tileset: null,
    tilesetPosition: [120, 30, 0]
  }
  /**
   * 处理Tileset
   * 同一个DbId对应的集合
   */
  function processTileset(tileset: any) {
    var _hiddenDbIds = []

    /**
     * 获取DbId
     * @param {*} feature
     */
    function getFeatureDbId(feature: any) {
      if (LSGlobe.defined(feature) && LSGlobe.defined(feature.getProperty)) {
        return parseInt(feature.getProperty('DbId'), 10)
      }
      return -1
    }
    function unloadFeature(_feature: any) {}

    function loadFeature(feature: any) {
      const dbId = getFeatureDbId(feature)
      // 存储dbId和feature的映射关系
      if (!tileset.dbIdToFeatures) tileset.dbIdToFeatures = {}
      let features = tileset.dbIdToFeatures[dbId]
      if (!LSGlobe.defined(features)) {
        tileset.dbIdToFeatures[dbId] = features = []
      }
      features.push(feature)
    }

    function processContentFeatures(content: any, callback: any) {
      const featuresLength = content.featuresLength
      for (let i = 0; i < featuresLength; ++i) {
        const feature = content.getFeature(i)
        callback(feature)
      }
    }

    function processTileFeatures(tile: any, callback: any) {
      const content = tile.content
      const innerContents = content.innerContents
      if (LSGlobe.defined(innerContents)) {
        const length = innerContents.length
        for (let i = 0; i < length; ++i) {
          processContentFeatures(innerContents[i], callback)
        }
      } else {
        processContentFeatures(content, callback)
      }
    }

    tileset.tileLoad.addEventListener(function (tile: any) {
      processTileFeatures(tile, loadFeature)
    })

    tileset.tileUnload.addEventListener(function (tile: any) {
      processTileFeatures(tile, unloadFeature)
    })
  }
  function getTileSetPosition(tileset: any) {
    //用模型编辑器获取(目前获取的是建模原点的位置)
    const getPositionByModelEditor = () => {
      viewer!.boxEditBoxGeometry.viewModel.editObject = tileset
      const pos = cloneDeep(viewer!.boxEditBoxGeometry.viewModel.position)
      viewer!.boxEditBoxGeometry.viewModel.editObject = null
      return [pos.x, pos.y, pos.z]
    }
    return getPositionByModelEditor()
  }
}

加载样式

加载样式使用方法

倾斜模型属性类型

ts
init_obliqueModel_style(tileset, attr)
function init_obliqueModel_style<T extends obliqueModelAttributes>(
  tileset: any,
  attr: T
) {
  if (attr.modelMatrix) {
    tileset.modelMatrix = LSGlobe.Matrix4.fromArray(attr.modelMatrix)
  }
  tileset.style = new LSGlobe.Cesium3DTileStyle({
    color: "color('rgba(255,255,255," + (attr.opacity ?? 100) / 100 + ")')"
  })
  tileset.maximumScreenSpaceError = attr.granularity
}

保存属性

ts
/**获取经纬高 */
const { lon, lat, height } = (() => {
  const formModel = {
    lon: 120,
    lat: 30,
    height: 0
  }
  viewer.boxEditBoxGeometry.viewModel.editObject = formModel.tileset
  viewer.boxEditBoxGeometry.viewModel.move()
  const pos = viewer!.boxEditBoxGeometry.viewModel.position
  formModel.lon = pos.x
  formModel.lat = pos.y
  formModel.height = pos.z
  viewer!.boxEditBoxGeometry.viewModel.editObject = null
  return formModel
})()
saveAttr<obliqueModelAttributes>({
  lon: lon,
  lat: lat,
  height: height,
  opacity: 100,
  granularity: 10,
  modelMatrix: LSGlobe.Matrix4.toArray(tileset.modelMatrix) //保存模型位置的矩阵
})

las/laz

bizTypefileType
4las
4laz

las/laz 属性类型

ts
export type lasAttributes = {
  lon?: number // 经度
  lat?: number // 纬度
  height?: number // 海拔高度
  granularity?: number // 精细度
  modelMatrix?: number[] //模型矩阵
  pointSize?: number // 点大小1~10
  /**
   * las着色模式
   * 0: 真彩色
   * 1: 强度色
   * 2: 回波次数色
   * 3: 高程彩色
   * 4:分类设色
   */
  lasTintMode?: (typeof lasRenderModes)[number]['value']
  /**样式 */
  style?: Record<string, any>
  /**分类设色配置
   * [[分类号,分类名,颜色],...]
   */
  lasTintClassifications?: string[][]
}
/**las着色模式 */
export const lasRenderModes = [
  {
    label: '真彩色',
    value: 0
  },
  {
    label: '强度色',
    value: 1
  },
  {
    label: '回波次数色',
    value: 2
  },
  {
    label: '高程彩色',
    value: 3
  },
  {
    label: '分类着色',
    value: 4
  }
] as const

/**las额外属性 */
export type lasExtra = {
  maxIntensity?: number
  minIntensity?: number
  Classification?: string
  maxReturnNumber?: number
  extrasVersion?: number
  maxElevation?: number
  minElevation?: number
  realColor?: number //真彩色,没有真彩色则没有这个字段
}

/**las默认分类 */
export const las_default_classifications = [
  ['0', '未分类', '#FF8080'],
  ['1', '地面', '#804000'],
  ['2', '建筑物', '#FF8000'],
  ['3', '低植被', '#008000'],
  ['4', '中等植被', '#00C800'],
  ['5', '高植被', '#00FF00'],
  ['6', '水体', '#0000FF'],
  ['7', '道路', '#808080'],
  ['8', '车辆', '#FF00FF'],
  ['9', '行人', '#FF0080'],
  ['10', '铁路', '#808080'],
  ['11', '桥梁', '#AAAAAA'],
  ['12', '高压线', '#FFFF00'],
  ['13', '输电塔', '#00FFFF'],
  ['14', '其他', '#FF8080']
] as const

加载 las/laz

加载倾斜模型

加载样式

加载样式使用方法

las/laz 属性类型

ts
init_las_style(tileset, attr)
ts
/**
 * 初始化las样式
 * @param tileset
 * @param attr
 */
export function init_las_style<T extends lasAttributes>(tileset: any, attr: T) {
  if (attr.modelMatrix) {
    tileset.modelMatrix = LSGlobe.Matrix4.fromArray(attr.modelMatrix)
  }
  if (!attr.style) {
    tileset.style = new LSGlobe.Cesium3DTileStyle(
      gen_las_style(
        tileset,
        attr.pointSize,
        attr.lasTintMode,
        attr.lasTintClassifications
      )
    )
  } else {
    tileset.style = new LSGlobe.Cesium3DTileStyle(attr.style)
  }

  tileset.maximumScreenSpaceError = attr.granularity
}
/**生成las样式 */
export function gen_las_style(
  tileset: any,
  pointSize: number | undefined,
  tintMode: lasProps['lasTintMode'],
  classifications?: lasProps['lasTintClassifications']
) {
  const colorStyle = get_las_tint_mode_color_style(
    tileset.extras,
    tintMode,
    classifications
  )
  return {
    pointSize: pointSize ?? 4,
    ...colorStyle
  }
}
/**生成las着色模式样式 */
export function get_las_tint_mode_color_style(
  lasTilesetExtras: lasExtra,
  tintMode?: lasProps['lasTintMode'],
  classifications?: lasProps['lasTintClassifications']
) {
  if (isVoid(lasTilesetExtras) || isVoid(tintMode)) {
    console.warn('未设置las渲染模式')
    return {}
  }
  switch (tintMode) {
    case 0:
      return {}
    case 1:
      return createIntensityStyle()
    case 2:
      return createReturnNumberStyle()
    case 3:
      return createElevationStyle()
    case 4:
      return createClassificationStyle()
    default:
      return {}
  }
  /**强度色 */
  function createIntensityStyle() {
    const maxIntensity = lasTilesetExtras?.maxIntensity
    const minIntensity = lasTilesetExtras?.minIntensity
    if (isVoid(maxIntensity) || isVoid(minIntensity)) {
      console.warn('未设置las强度范围')
      return {}
    }
    const delta =
      maxIntensity <= minIntensity ? 0.25 : (maxIntensity - minIntensity) / 4.0

    return {
      color: {
        conditions: [
          [`\${INTENSITY} >= ${maxIntensity}`, "color('rgba(255,0,0,1)')"], // 红色
          [
            `\${INTENSITY} >= ${maxIntensity - delta}`,
            "color('rgba(255,255,0,1)')"
          ], // 黄色
          [
            `\${INTENSITY} >= ${maxIntensity - 2 * delta}`,
            "color('rgba(0,255,0,1)')"
          ], // 绿色
          [
            `\${INTENSITY} >= ${maxIntensity - 3 * delta}`,
            "color('rgba(0,255,255,1)')"
          ], // 青色
          [
            `\${INTENSITY} >= ${maxIntensity - 4 * delta}`,
            "color('rgba(0,0,255,1)')"
          ],
          ['true', "color('rgba(0,0,255,1)')"] // 蓝色
        ]
      }
    }
  }
  /**回波次数色 */
  function createReturnNumberStyle() {
    const maxReturnNumber = lasTilesetExtras?.maxReturnNumber
    if (isVoid(maxReturnNumber)) {
      console.warn('未设置las回波次数')
      return {}
    }
    if (maxReturnNumber > 2) {
      const midValue = Math.ceil((maxReturnNumber + 1) / 2.0)
      return {
        color: {
          conditions: [
            [`\${ECHO} === ${maxReturnNumber}`, "color('rgba(255,0,0,1)')"], // 最大回波红色
            [`\${ECHO} === ${midValue}`, "color('rgba(0,255,0,1)')"], // 中间回波绿色
            [`\${ECHO} === 1`, "color('rgba(0,0,255,1)')"], // 最小回波蓝色
            ['true', "color('rgba(0,0,0,1)')"] // 最小回波黑色
          ]
        }
      }
    } else if (maxReturnNumber === 2) {
      return {
        color: {
          conditions: [
            [`\${ECHO} === 2`, "color('rgba(0,255,0,1)')"], // 回波2绿色
            [`\${ECHO} === 1`, "color('rgba(0,0,255,1)')"], // 最小回波蓝色
            ['true', "color('rgba(0,0,255,1)')"] // 回波1蓝色
          ]
        }
      }
    } else {
      return {
        color: "color('rgba(0,0,255,1)')" // 单回波全部蓝色
      }
    }
  }
  /**分类着色 */
  function createClassificationStyle() {
    const classification = lasTilesetExtras?.Classification
    if (isVoid(classification)) {
      console.warn('未设置las分类')
      return {}
    }
    if (classifications) {
      return {
        color: {
          conditions: classifications.map((e) => [
            `\${CLASSIFICATION} === ${e[0]}`,
            `color('${e[2]}')`
          ])
        }
      }
    } else {
      return {}
    }
  }
  // 新增颜色工具函数
  function generateGradientColors(count: number): string[] {
    const colors: string[] = []
    for (let i = 0; i < count; i++) {
      // HSL: 从蓝色(240)到红色(0)
      const hue = (i * 240) / (count - 1)
      colors.push(`rgba(${HSLToRGB(hue, 100, 50).join(',')},1)`)
    }
    return colors
  }

  function HSLToRGB(h: number, s: number, l: number): [number, number, number] {
    s /= 100
    l /= 100
    const k = (n: number) => (n + h / 30) % 12
    const a = s * Math.min(l, 1 - l)
    const f = (n: number) =>
      l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))
    return [
      Math.round(255 * f(0)),
      Math.round(255 * f(8)),
      Math.round(255 * f(4))
    ]
  }

  //高程着色
  function createElevationStyle() {
    const maxElevation = lasTilesetExtras?.maxElevation
    const minElevation = lasTilesetExtras?.minElevation
    if (isVoid(maxElevation) || isVoid(minElevation)) {
      console.warn('未设置las高程范围')
      return {}
    }
    const SEGMENTS = 50 // 区间数量
    const delta =
      maxElevation <= minElevation
        ? 0.25
        : (maxElevation - minElevation) / SEGMENTS
    const colors = generateGradientColors(SEGMENTS + 1)

    const conditions = []
    for (let i = 0; i < SEGMENTS; i++) {
      conditions.push([
        `\${ELEVATION} >= ${maxElevation - i * delta}`,
        `color('${colors[i]}')`
      ])
    }
    conditions.push(['true', `color('${colors[SEGMENTS]}')`])

    return {
      color: {
        conditions
      }
    }
  }
}
/**生成las默认分类 */
export function gen_default_classfications(lasTilesetExtras: lasExtra) {
  const classification = lasTilesetExtras?.Classification
  if (isVoid(classification)) {
    console.warn('未设置las分类')
    return undefined
  }
  const classificationsTypes = classification
    .split(';')
    .map((e) => Number(e.split(',')[0]))
  return classificationsTypes.map((e) => [
    e.toString(),
    las_default_classifications[e]?.[1] ?? '其他',
    las_default_classifications[e]?.[2] ?? '#FF8080'
  ])
}
/**获取可启用的着色模式 */
export function get_las_tint_mode_enable_arr(lasTilesetExtras?: lasExtra) {
  const arr: number[] = []
  if (isVoid(lasTilesetExtras)) {
    return arr
  }
  const classification = lasTilesetExtras?.Classification
  const maxReturnNumber = lasTilesetExtras?.maxReturnNumber
  const maxIntensity = lasTilesetExtras?.maxIntensity
  const minIntensity = lasTilesetExtras?.minIntensity
  const realColor = lasTilesetExtras?.realColor
  const maxElevation = lasTilesetExtras?.maxElevation
  const minElevation = lasTilesetExtras?.minElevation
  if (!isVoid(classification)) {
    arr.push(4)
  }
  if (!isVoid(maxReturnNumber)) {
    arr.push(2)
  }
  if (!isVoid(maxIntensity) && !isVoid(minIntensity)) {
    arr.push(1)
  }
  if (!isVoid(realColor)) {
    arr.push(0)
  }
  if (!isVoid(maxElevation) && !isVoid(minElevation)) {
    arr.push(3)
  }
  return arr.sort((a, b) => a - b)
}
function isVoid(val: unknown) {
  return val === undefined || val === null || val === ''
}

保存属性

保存属性使用方法/类型

保存素材属性通用

las/laz 属性类型

ts
/**获取经纬高 */
const { lon, lat, height } = (() => {
  const formModel = {
    lon: 120,
    lat: 30,
    height: 0
  }
  viewer.boxEditBoxGeometry.viewModel.editObject = formModel.tileset
  viewer.boxEditBoxGeometry.viewModel.move()
  const pos = viewer!.boxEditBoxGeometry.viewModel.position
  formModel.lon = pos.x
  formModel.lat = pos.y
  formModel.height = pos.z
  viewer!.boxEditBoxGeometry.viewModel.editObject = null
  return formModel
})()
saveAttr<lasAttributes>({
  lon: lon,
  lat: lat,
  height: height,
  granularity: 10,
  pointSize: 4,
  modelMatrix: LSGlobe.Matrix4.toArray(tileset.modelMatrix), //保存模型位置的矩阵
  lasTintMode: 1,
  style: gen_las_style(
    formModel.tileset,
    formModel.pointSize,
    formModel.lasTintMode,
    formModel.lasTintClassifications
  ),
  lasTintClassifications: gen_default_classfications(formModel.tileset.extras)
})

BIM

bizType
5

BIM 属性类型

ts
type bimAttributes = {
  lon?: number // 经度
  lat?: number // 纬度
  height?: number // 海拔高度
  modelMatrix?: number[] //模型矩阵
  opacity?: number // 不透明度
  granularity?: number // 精细度
  hasProjection?: boolean // 是否有投影
}

加载 BIM

加载倾斜模型

加载样式

ts
init_bim_style(tileset, attr, hasProjection)
ts
function init_bim_style<T extends bimAttributes>(
  tileset: any,
  attr: T,
  hasProjection: boolean
) {
  //没有投影的时候才设置模型矩阵
  if (attr.modelMatrix && hasProjection === false) {
    tileset.modelMatrix = LSGlobe.Matrix4.fromArray(attr.modelMatrix)
  }
  tileset.style = new LSGlobe.Cesium3DTileStyle({
    color: "color('rgba(255,255,255," + (attr.opacity ?? 100) / 100 + ")')"
  })
  tileset.maximumScreenSpaceError = attr.granularity
}
/**判断BIM是否有投影[ hasProjection ]*/
async function is_bim_has_projection(folderId: string) {
  const res = await getLastPublishConfig(folderId)
  if (!res?.data?.cfgValue) {
    return false
  }
  try {
    const val = JSON.parse(decodeURIComponent(res.data.cfgValue))
    console.log('bim PROJ CFG', val)
    if (
      val?.['SHOW_PRJ'] === false ||
      val?.['hasProjection'] === false ||
      !val
    ) {
      return false
    } else {
      return true
    }
  } catch (error) {
    console.error(error)
  }
  return false
}
function getLastPublishConfig(folderId: string) {
  return request({
    url: '/admin/tx/data/getLastDataConfig',
    method: 'get',
    params: {
      folderId
    }
  })
}

保存属性

保存属性使用方法/类型

保存素材属性通用

BIM 属性类型

ts
/**获取经纬高 */
const { lon, lat, height } = (() => {
  const formModel = {
    lon: 120,
    lat: 30,
    height: 0
  }
  viewer.boxEditBoxGeometry.viewModel.editObject = formModel.tileset
  viewer.boxEditBoxGeometry.viewModel.move()
  const pos = viewer!.boxEditBoxGeometry.viewModel.position
  formModel.lon = pos.x
  formModel.lat = pos.y
  formModel.height = pos.z
  viewer!.boxEditBoxGeometry.viewModel.editObject = null
  return formModel
})()
saveAttr<bimAttributes>({
  lon: lon,
  lat: lat,
  height: height,
  opacity: 100,
  granularity: 10,
  hasProjection: true,
  modelMatrix: LSGlobe.Matrix4.toArray(tileset.modelMatrix) //保存模型位置的矩阵
})

shp

bizTypefileType
6shp

shp 属性类型

ts
type shpAttributes = {
  attachType?: number //依附模式
  styles?: {
    styles: Record<string, any>[]
  } //样式
  selectedStyles?: {
    styles: Record<string, any>[]
  } //选中的样式
}

加载 shp

加载 shp 需要的参数

获取加载参数

ts
/**
 * 加载shp
 * @param id 数据id
 * @param jsonPath shpjson路径
 */
async function loadShp({ id, jsonPath }: { id: string; jsonPath: string }) {
  try {
    const currentImageLayer = await (LSGlobe as any).VectorTileProvider.fromUrl(
      jsonPath,
      { viewer: viewer, maximumLevel: 30 }
    )
    const shptype = await fnGetShpAttributeByMaterilId(currentImageLayer, id)
    const imageryLayer = viewer!.imageryLayers.addImageryProvider(
      currentImageLayer,
      3
    )
    var rect = currentImageLayer._rectangle
    const flyFn = () =>
      viewer!.camera.flyTo({
        destination: rect
      })
    flyFn()
    return { imageryLayer, shptype }
  } catch (error) {
    console.error(`Error loading vector tiles: ${error}`)
  }
  return null
  async function fnGetShpAttributeByMaterilId(
    imageryprovider: any,
    materialid: any
  ) {
    const oStyle = {
      styletyle: 'default',
      color: 'rgba(33, 150, 243,0.3)',
      lineType: (LSGlobe as any).VectorTileLineType.SOLID,
      lineWidth: 1,
      outline: true,
      outlineColor: '#2196F3',
      icon: '/scene/images/GEMarker/red-circle.png',
      textField: 'NAME',
      font: "normal 16px 'Microsoft YaHei'"
    }

    const oSelectedStyle = {
      color: 'rgba(255, 87, 34,0.1)',
      lineType: (LSGlobe as any).VectorTileLineType.SOLID,
      lineWidth: 1,
      outline: true,
      outlineColor: '#FF5722',
      icon: '/scene/images/GEMarker/red-circle.png',
      textField: 'NAME',
      font: "normal 16px 'Microsoft YaHei'"
    }
    try {
      const res = await getShpAttrByFolderId(materialid)
      if (res.code == 0) {
        if (!res.data) {
          console.warn('获取shp素材的属性为空')
          return 1
        }
        var sGeometrytype = res.data.geometryType
        var oStylePortion = { ...oStyle } as any
        var oSelectedStylePortion = { ...oSelectedStyle } as any

        if (sGeometrytype.indexOf('POINT') >= 0) {
          oStylePortion.color = 'rgba(255, 255, 255,1)'
          oStylePortion.outlineColor = '#000000'
          oStylePortion.outline = true
          oStylePortion.lineWidth = 3
          oStylePortion.font = 'normal 16px 微软雅黑'
          oStylePortion.textColor = '#FFFFFF'
          oStylePortion.textField = undefined
          oStylePortion.textOutlineColor = '#000000'
          oSelectedStylePortion.lineWidth = 3
          oSelectedStylePortion.color = 'rgba(255, 0, 0,1)'
          oSelectedStylePortion.outlineColor = '#000000'
        } else if (sGeometrytype.indexOf('LINESTRING') >= 0) {
          oStylePortion.lineWidth = 4
          oSelectedStylePortion.lineWidth = 4
          oStylePortion.color = 'rgba(251,188,4,1)'
          oSelectedStylePortion.color = 'rgba(255, 87, 34,1)'
          delete oStylePortion.icon
          delete oStylePortion.textField
          delete oSelectedStylePortion.icon
          delete oSelectedStylePortion.textField
        } else if (sGeometrytype.indexOf('POLYGON') >= 0) {
          delete oStylePortion.icon
          delete oStylePortion.textField
          delete oSelectedStylePortion.icon
          delete oSelectedStylePortion.textField
        }

        var styles = new (LSGlobe as any).VectorTileStyleCollection([
          new (LSGlobe as any).VectorTileStyle(oStylePortion)
        ])

        var selectedstyles = new (LSGlobe as any).VectorTileStyleCollection([
          new (LSGlobe as any).VectorTileStyle(oSelectedStylePortion)
        ])

        imageryprovider.styles = styles
        imageryprovider.selectedStyles = selectedstyles
        return getShpType(sGeometrytype)
      } else {
        console.error('查询不到该大矢量数据信息')
      }
    } catch (err) {
      console.log(err)
    }
    return 1
  }
  function getShpType(sGeometrytype: string) {
    if (sGeometrytype.indexOf('POINT') >= 0) {
      return 1
    } else if (sGeometrytype.indexOf('LINESTRING') >= 0) {
      return 2
    } else if (sGeometrytype.indexOf('POLYGON') >= 0) {
      return 3
    }
    return 1
  }
  function getShpAttrByFolderId(folderId: string) {
    return request({
      url: '/admin/tx/shp/getAttributeByFolderId',
      method: 'get',
      params: {
        folderId
      }
    })
  }
}

加载样式

加载样式使用方法

shp 属性类型

ts
function init_shp_style(oImagery: any, styles?: any[], selectedStyles?: any[]) {
  if (styles) {
    oImagery.imageryProvider.styles = new (
      LSGlobe as any
    ).VectorTileStyleCollection(
      cloneDeep(styles).map((s) => {
        return new (LSGlobe as any).VectorTileStyle(s)
      })
    )
  }
  if (selectedStyles) {
    oImagery.imageryProvider.selectedStyles = new (
      LSGlobe as any
    ).VectorTileStyleCollection(
      cloneDeep(selectedStyles).map((s) => {
        return new (LSGlobe as any).VectorTileStyle(s)
      })
    )
  }
}

style 书写规则

点要素样式(shptype=1)

属性全局模式分类模式值类型说明
textColorstring文字颜色-rgba 字符串
fontstringnormal 16px 'Microsoft YaHei' 类似 css 的 Font
outlinebooleantrue
outlineColorstring边线颜色-rgba 字符串
textOutlineColorstring边线颜色-rgba 字符串
lineWidthnumber边线宽度
textFieldstring显示文字的字段
iconstring图标 url
classificationField×string分类的字段
classificationFieldValue×string分类的值

线要素样式(shptype=2)

属性全局模式分类模式值类型说明
colorstring边线颜色-rgba 字符串
lineTypestring/string[]0,[2,2],[6,2]
outlineboolean是否有边线
lineWidthnumber边线宽度
textFieldundefinedundefined
classificationField×string分类的字段
classificationFieldValue×string分类的值

面要素样式(shptype=3)

属性全局模式分类模式值类型说明
colorstring面颜色 rgba 字符串
lineTypestring/string[]0,[2,2],[6,2]
lineWidthnumber边线宽度
outlineboolean是否有边线
outlineColorstring边线颜色-rgba 字符串
textFieldundefinedundefined
classificationField×string分类的字段
classificationFieldValue×string分类的值

样式解释

全局模式:全局模式下的属性会应用到所有要素上

json
[
  {
    "color": "RGBA(251, 188, 4, 1)",
    "lineWidth": 4,
    "lineType": 0,
    "outline": true,
    "outlineColor": "#000000"
  }
]

分类模式:分类模式下的属性会根据分类的字段和值来应用到对应的要素上

json
[
  {
    "outline": true,
    "lineWidth": 3,
    "outlineColor": "#000000",
    "icon": "/scene/images/GEMarker/red-circle.png",
    "textField": "ADDRESS",
    "textColor": "RGBA(145, 220, 203, 1)",
    "textOutlineColor": "#000000",
    "font": "normal 16px 'Microsoft YaHei'",
    "classificationField": "ZHONGLEI",
    "classificationFieldValue": "7301"
  },
  {
    "outline": true,
    "lineWidth": 3,
    "outlineColor": "#000000",
    "icon": "/scene/images/GEMarker/red-circle.png",
    "textField": "ADDRESS",
    "textColor": "RGBA(140, 32, 106, 1)",
    "textOutlineColor": "#000000",
    "font": "normal 16px 'Microsoft YaHei'",
    "classificationField": "ZHONGLEI",
    "classificationFieldValue": "7304"
  },
  {
    "color": "#FFFFFF",
    "lineWidth": 3,
    "lineType": [],
    "outline": true,
    "outlineColor": "#000000",
    "icon": "/scene/images/GEMarker/red-circle.png",
    "textField": "ADDRESS",
    "textColor": "RGBA(255, 255, 255, 1)",
    "textOutlineColor": "#000000",
    "font": "normal 16px 'Microsoft YaHei'"
  }
]

classificationField/classificationFieldValue 获取

ts
//例子
const classificationField = await fnGetFields(id)
const classificationFieldValues = await queryShpGeometryType({
  columnName: fields[0],
  folderId: id
})

/**获取shp某个字段的值 */
function queryShpGeometryType(data: {
  columnName?: string
  folderId?: string
}) {
  return request({
    url: '/admin/tx/shp/queryShpGeometryType',
    method: 'get',
    params: data
  })
}
/**获取shp所有字段 */
async function fnGetFields(id: string) {
  try {
    const result = await getDataIndex({ dataId: id })
    let oFieldList = result['vector_layers'][0]['fields']
    let not_allowed_attr = [
      'layer',
      'clustered',
      'clustered',
      'sqrt_point_count',
      'point_count',
      'tx_center_x',
      'tx_center_y'
    ]
    not_allowed_attr.forEach((item) => {
      delete oFieldList[item]
    })
    return Object.keys(oFieldList)
  } catch (err) {
    console.log(err)
  }
  function getDataIndex(params: { dataId: string }) {
    const token = CONSTANTS.TOKEN
    return request({
      url: `/admin/tx/preview/data/index/${params.dataId}`,
      method: 'get',
      params: {
        token
      }
    })
  }
}
ts
/**
 * 设置shp的样式
 * @param oImagery
 * @param aStyle 样式数组,来自上表
 */
function fnImagerySetStyles(oImagery: any, aStyle: any) {
  var aStylesPotion = []
  for (var i = 0; i < aStyle.length; i++) {
    aStylesPotion.push(new (LSGlobe as any).VectorTileStyle(aStyle[i]))
  }
  var styles = new (LSGlobe as any).VectorTileStyleCollection(aStylesPotion)
  oImagery.imageryProvider.styles = styles
}

保存属性

保存属性使用方法/类型

保存素材属性通用

shp 属性类型

ts
const styles = oImagery.imageryProvider.styles.toJson()
const selectedStyles = oImagery.imageryProvider.selectedStyles.toJson()
saveAttr<shpAttributes>({
  attachType: attachType.value,
  styles,
  selectedStyles
})

kml/kmz

bizTypefileType
6kml
6kmz

加载 kml

ts
/**
 * 加载kml/kmz
 * @param jsonPath kml/kmz路径
 */
async function load_kml_kmz(jsonPath: string) {
  try {
    const dataSource = await LSGlobe.KmlDataSource.load(jsonPath, {
      camera: viewer!.scene.camera,
      canvas: viewer!.scene.canvas
    })
    viewer!.dataSources.add(dataSource)
    const flyFn = () => viewer!.flyTo(dataSource)
    flyFn()
    //关闭地形深度,需要在加载别的素材的时候打开
    viewer!.scene.globe.depthTestAgainstTerrain = false
  } catch (error) {
    console.error(`Error loading tileset: ${error}`)
  }
}

属性类型

加载样式

保存属性

保存属性使用方法/类型

保存素材属性通用

ts
saveAttr({})

cad/lcad

bizTypefileType
6dwg
6lcad
6dxf

cad/lcad 属性类型

ts
type cadAttributes = {
  attachType?: number //1:依模型  0:依地表
  opacity?: number //不透明度
}

加载 cad/lcad

ts
/**
 * 加载cad/lcad
 * @param jsonPath
 */
async function loadCad(jsonPath: string) {
  const { imageryLayers } = viewer!
  try {
    const imageryProvider = await (LSGlobe as any).LSImageryProvider.fromUrl(
      jsonPath
    )
    const imageryLayer = imageryLayers.addImageryProvider(imageryProvider)

    const flyFn = () =>
      viewer!.camera.flyTo({
        destination: imageryProvider.rectangle
      })
    flyFn()

    return { imageryLayer }
  } catch (error) {
    console.error(`Error loading tileset: ${error}`)
  }
  return { imageryLayer: null }
}

加载样式

加载样式使用方法

加载 cad/lcad

ts
function init_cad_style<T extends cadAttributes>(imageryLayer: any, attr: T) {
  imageryLayer.alpha = (attr.opacity ?? 100) / 100
}

保存属性

保存属性使用方法/类型

保存素材属性通用

cad/lcad 属性类型

ts
saveAttr<cadAttributes>({
  attachType: 1,
  opacity: 100
})

gltf/glb

bizTypefileType
8gltf
8glb

gltf/glb 属性类型

ts
type glbAttributes = {
  lon?: number // 经度
  lat?: number // 纬度
  height?: number // 海拔高度
  modelMatrix?: number[] //模型矩阵
  rotateZ?: number // 旋转角度
  length?: number // 长
  width?: number // 宽
  modelHeight?: number // 高
  scale?: number // 缩放0~Infinity,默认1
}

加载 gltf/glb

加载 gltf/glb 需要的参数

获取加载参数

ts
/**
 * 加载glb/gltf
 * @param jsonPath glb/gltf路径
 * @param position
 */
async function loadGlbGltf({
  jsonPath,
  position
}: {
  jsonPath: string
  position: string
}) {
  const positionArr = (position?.split(',')?.map((e) => Number(e) ?? 0) ?? [
    120, 30, 0
  ]) as [number, number, number]
  const origin = LSGlobe.Cartesian3.fromDegrees(...positionArr)
  const heading = LSGlobe.Math.toRadians(0.0)
  const pitch = LSGlobe.Math.toRadians(0.0)
  const roll = LSGlobe.Math.toRadians(0.0)
  const hpr = new LSGlobe.HeadingPitchRoll(heading, pitch, roll)
  const modelMatrix = LSGlobe.Transforms.headingPitchRollToFixedFrame(
    origin,
    hpr
  )
  const loader = () => {
    return new Promise<{
      model: any
      positionArr: [number, number, number]
      modelMatrix: number[]
    }>(async (resolve, reject) => {
      try {
        const gltf = await (LSGlobe.Model as any).fromGltfAsync({
          url: jsonPath,
          modelMatrix
        })
        const model = viewer!.scene.primitives.add(gltf)
        model.readyEvent.addEventListener(async () => {
          model.activeAnimations.addAll({
            loop: LSGlobe.ModelAnimationLoop.REPEAT,
            multiplier: 1
          })

          resolve({
            model,
            positionArr,
            modelMatrix: LSGlobe.Matrix4.toArray(modelMatrix)
          })
        })
      } catch (error) {
        reject(error)
      }
    })
  }
  try {
    const res = await loader()
    return res
  } catch (error) {
    console.error('loadGlbGltf error:', error)
  }
  return { model: null, positionArr: null, modelMatrix: null }
}

加载样式

加载样式使用方法

gltf/glb 属性类型

ts
function init_glb_style<
  T extends {
    modelMatrix?: number[]
    scale?: number
  }
>(model: any, attr: T) {
  const { modelMatrix } = attr
  if (!modelMatrix) return
  model.modelMatrix = LSGlobe.Matrix4.fromArray(modelMatrix)
  if (attr.scale !== undefined || attr.scale !== null) {
    model.scale = attr.scale
  }
}

保存属性

保存属性使用方法/类型

保存素材属性通用

gltf/glb 属性类型

ts
/**获取经纬高 */
const { lon, lat, height } = (() => {
  const formModel = {
    lon: 120,
    lat: 30,
    height: 0
  }
  viewer.boxEditBoxGeometry.viewModel.editObject = formModel.tileset
  viewer.boxEditBoxGeometry.viewModel.move()
  const pos = viewer!.boxEditBoxGeometry.viewModel.position
  formModel.lon = pos.x
  formModel.lat = pos.y
  formModel.height = pos.z
  viewer!.boxEditBoxGeometry.viewModel.editObject = null
  return formModel
})()
/**更新旋转 */
function updateRotateZ(rotateZ: number) {
  viewer!.boxEditBoxGeometry.viewModel.editObject = model
  viewer!.boxEditBoxGeometry.viewModel.rotation = new LSGlobe.Cartesian3(
    0,
    0,
    rotateZ
  )
  viewer!.boxEditBoxGeometry.viewModel.editObject = null
}
function getModelBoundingSize(model: any) {
  if (!model) return 1

  try {
    // 获取模型包围球
    const boundingSphere = model.boundingSphere
    if (!boundingSphere) return 1

    // 返回包围球半径作为默认尺寸
    return boundingSphere.radius || 1
  } catch (error) {
    console.warn('获取模型默认尺寸失败:', error)
    return 1
  }
}
const size = getModelBoundingSize(model)
saveAttr<glbAttributes>({
  lon: lon,
  lat: lat,
  height: height,
  modelMatrix: LSGlobe.Matrix4.toArray(model.modelMatrix),
  rotateZ: 0,
  length: size,
  width: size,
  modelHeight: size,
  scale: model.scale
})

Released under the MIT License.