超图使用用例合集

545 阅读3分钟

设计一个SuperMap3DLayer类来汇总超图的功能点

用于汇总超图的功能点,这样可以方便地管理和使用超图的三维地图功能。同时这个类可以封装超图提供的各种三维地图操作,如加载三维图层、进行空间分析、实现交互式操作等。

一、 自定义事件监听

  • 通过事件监听,可以实现模块间的解耦,提高代码的可维护性和可扩展性。
  • 自定义事件监听允许开发者根据特定需求来定义事件和响应。
  • 通过自定义事件监听,可以捕捉用户的操作,如点击,扩展超图的点击。

1. 自己实现发布订阅的事件

  
export default class SuperMap3DLayer {
    private subscribe: Map<string, Array<Function>>;

    constructor(options?: any) {
        this.subscribe = new Map();
    }

    public static on(event: BaseMapEventEnum, callback: Function) {
        if (!event) return;
        const sub = this.subscribe.get(event.toString()) || [];
        sub.push(callback);
        this.subscribe.set(event.toString(), sub);
    }

    public static emit(type: BaseMapEventEnum, ...args: any[]) {
        const events = this.subscribe.get(type.toString());
        if (events) {
            events.forEach((el) => el(...args));
        }
    }

    public static off(event: BaseMapEventEnum, callback: Function) {
        const events = this.subscribe.get(event.toString());
        if (events) {
            const index = events.indexOf(callback);
            if (index !== -1) {
                events.splice(index, 1);
            }
        }
    }

    public destroy() {
        this.subscribe.clear();
    }
}

使用方法:

  • 监听事件:SuperMap3DLayer.on('resetMap', (e) => console.log);
  • 发送事件:SuperMap3DLayer.emit('resetMap', false);

2、使用mitt 【推荐😄😄😄】

import mitt, { Emitter, EventType } from 'mitt';
export default class SuperMap3DLayer {
    static emitter: Emitter<Record<EventType, unknown>>;
    constructor(options?: SuperMap3DLayerOptions) {  
        SuperMap3DLayer.emitter = mitt();  
    }
}

使用方法:

  • 监听事件:SuperMap3DLayer.emitter.on('resetMap', (e) => console.log);

  • 发送事件:SuperMap3DLayer.emitter.emit('resetMap', false);

二、 基础图层功能

1. 使用天地图初始化超图 ✨

const URL_CONFIG = {
  TOKEN_TIANDITU: '天地图TOKEN',
  TDT_VEC: '//t0.tianditu.gov.cn/vec_w/wmts', // 天地图影像
  TDT_IMG: '//[subdomain].tianditu.com/img_w/wmts', // 天地图影像
  TDT_LABEL: '//{s}.tianditu.gov.cn/mapservice', // 天地图文字注记
};

initSuperMap() {
    // 初始化
    SuperMap3DLayer.viewer = new SuperMap3D.Viewer(SuperMap3DLayer.options.container, {  
        skyAtmosphere: false,  // 自行根据业务调整
        skyBox: false,   // 自行根据业务调整
        InfoBox: false,   // 自行根据业务调整
    });
    // 设置背景
    SuperMap3DLayer.viewer.scene.backgroundColor = new SuperMap3D.Color(0.0, 0.0, 0.0, 0.0);

    // 设置相机
    SuperMap3DLayer.viewer.scene.camera.setView({
      destination: SuperMap3D.Cartesian3.fromDegrees(120.2, 30.3, 1000000.0),
    });
    // 缩放比例,以匹配设备的像素比例
    SuperMap3DLayer.viewer.resolutionScale = window.devicePixelRatio;
    // 添加影像图层
    SuperMap3DLayer.imagery_img_w = SuperMap3DLayer.viewer.imageryLayers.addImageryProvider(
      new SuperMap3D.TiandituImageryProvider({
        token: URL_CONFIG.TOKEN_TIANDITU,
      }),
    );
    // 添加矢量图层
    SuperMap3DLayer.imagery_vec_w = SuperMap3DLayer.viewer.imageryLayers.addImageryProvider(
      new SuperMap3D.TiandituImageryProvider({
        mapStyle: 'vec_w',
        token: URL_CONFIG.TOKEN_TIANDITU,
      }),
    );
    // 指定三维场景的默认底图样式
    SuperMap3DLayer.baseMapStyle = 'imagery_img_w';
    // 最大缩放距离,单位为米
    SuperMap3DLayer.viewer.scene.screenSpaceCameraController.maximumZoomDistance = 700000;
    // 环境光颜色
    SuperMap3DLayer.viewer.scene.lightSource.ambientLightColor = new SuperMap3D.Color(0.65, 0.65, 0.65, 1);
}

2. 实现底图切换 ✨

public static set baseMapStyle(mapStyle: string) {
    const styles = {
      imagery_img_w: 0,
      imagery_vec_w: 0,
    };
    for (const stylesKey in styles) {
      if (mapStyle === stylesKey) {
        SuperMap3DLayer[stylesKey].alpha = 1;
      } else {
        SuperMap3DLayer[stylesKey].alpha = 0;
      }
    }
}

3. 监听场景的缩放事件 ✨

public static bindScaleEvent() {
  SuperMap3DLayer.viewer.scene.camera.changed.addEventListener(
    throttle(() => {
      const cameraHeight = SuperMap3DLayer.viewer.scene.camera.positionCartographic.height;
      SuperMap3DLayer.emitter.emit('zoomRangeLevelWatch', cameraHeight);
    }, 500),
  );
};

4. 监听场景的鼠标移入事件 ✨

  public static bindMouseOverEvent() {
    const handler = new SuperMap3D.ScreenSpaceEventHandler(SuperMap3DLayer.viewer.canvas);
    // 过滤不需要监听移入的实体
    const blackList = ['2D_XGQ_PROTECT', ''];
    handler.setInputAction(
      debounce((event) => {
        const pickedObjects = SuperMap3DLayer.viewer.scene.drillPick(event.endPosition);
        let pickedObject = null;
        for (let i = 0; i < pickedObjects.length; i++) {
          const type = pickedObjects[i].id?.dataset?.type;
          if (type && !blackList.includes(type)) {
            pickedObject = pickedObjects[i];
          }
          if (i > 3) {
            break;
          }
        }
        if (SuperMap3D.defined(pickedObject) && pickedObject.id) {
          const picked = pickedObject.id;
          const { dataset } = picked;
          // 拿到移入对象的额外数据
        }
      }, 2),
      SuperMap3D.ScreenSpaceEventType.MOUSE_MOVE,
    );
  }

5. 监听场景的鼠标点击事件 ✨

public static bindClickEvent() {
    const handler = new SuperMap3D.ScreenSpaceEventHandler(SuperMap3DLayer.viewer.canvas);
    const blackList = ['2D_HXBHQFW_PROTECT', '2D_HJXTQ_PROTECT', '2D_XGQ_PROTECT', ''];
    handler.setInputAction((event) => {
      // @ts-ignore
      const pickedObjects = SuperMap3DLayer.viewer.scene.drillPick(event.position);
      let pickedObject = null;
      for (let i = 0; i < pickedObjects.length; i++) {
        const type = pickedObjects[i].id?.dataset?.type;
        if (type && !blackList.includes(type)) {
          pickedObject = pickedObjects[i];
        }
        if (i > 3) {
          break;
        }
      }
      if (SuperMap3D.defined(pickedObject) && pickedObject.id) {
        const picked = pickedObject.id;
        const { dataset } = picked;
          // 拿到点击对象的额外数据
      }
    }, SuperMap3D.ScreenSpaceEventType.LEFT_CLICK);
}

6. 渲染倾斜摄影图层

public static renderProtectObliquePhotography(url: string, only3D: boolean) {  
SuperMap3DLayer.viewer.scene.addS3MTilesLayerByScp(
    `${url}/datas/Combine/config`,
    {  
        autoSetView: !!only3D,  
        cullEnabled: false,  
        subdomains: [],  
        name: '3D_PROTECT',  
    },  
    1,  
)  
.then((layer) => {  
    layer.dataset = {  
        name: '3D_PROTECT',  
        pname: '倾斜摄影',  
    };    
}

7. 划线

/**  
* 绘制行政区划线  
* @param position [lng, lat, lng, lat, ....]
* @param isScale  是否在添加完成后画面缩放到此实体
* @param name  标识名称
*/
public static drawLine(position, isScale = false, name = '') {  
    const points = SuperMap3D.Cartesian3.fromDegreesArray(position);  
    const lineEntity = new SuperMap3D.Entity({  
        pname: `${name}线`,  
        polyline: {  
            positions: points,  
            width: 2,  
            clampToGround: true,  
            material: new SuperMap3D.Color(255 / 255.0, 235 / 255.0, 59 / 255.0, 0.7),  
        },  
    });  
  
    SuperMap3DLayer.viewer.entities.add(lineEntity); 
    if (isScale) {  
        SuperMap3DLayer.viewer.camera.setView(lineEntity);  
    }  
}

8. 划面

let zIndex = 1000;
public static drawPoly(polys: any[]) {
  const renderPolygon = (polygons, el, zIndex) => {
      SuperMap3DLayer.viewer.entities.add({
        name: '配置图层',
        pname: this.options.pname,
        position: SuperMap3D.Cartesian3.fromDegrees(el.center.x, el.center.y, zHeight),
        // 文字标记
        label: {
          fillColor: SuperMap3D.Color.BLACK,
          outlineColor: SuperMap3D.Color.WHITE,
          outlineWidth: 5,
          style: SuperMap3D.LabelStyle.FILL_AND_OUTLINE,
          font: '16px Helvetica',
          text: el.name,
          disableDepthTestDistance: 1200,
          distanceDisplayCondition: new SuperMap3D.DistanceDisplayCondition(10, 10000),
          pixelOffset: new SuperMap3D.Cartesian2(43, 0),
        },
        // 自定义图标
        billboard: {
          image: `/images/layer/${imgName}.png`,
          width: 43,
          height: 87,
          disableDepthTestDistance: 1200,
          distanceDisplayCondition: new SuperMap3D.DistanceDisplayCondition(10, 5000),
          scaleByDistance: new SuperMap3D.NearFarScalar(1.5e2, 1.0, 1.5e7, 0.0),
        },
        // 额外数据
        dataset: {
          ...this.options,
          pname: this.options.pname,
          active: false,
          name: el.name,
          center: el.center,
        },
        polygon: {
          hierarchy: SuperMap3D.Cartesian3.fromDegreesArray(el.point),
          outline: true,
          zIndex,
          ...polygons,
        },
      });
  };
  polys.forEach((el) => {
      const polygons = {
        clampToGround: true, // 多边形紧贴地形
        material: featColors[type].base,
        outlineColor: featColors[type].base,
        outlineWidth: 2.0,
      };
      renderPolygon(polygons, el, zIndex);
  }
}

9. 划体

// 面增加 hierarchy 即可
polygon: {
  hierarchy: SuperMap3D.Cartesian3.fromDegreesArrayHeights(el.point),
  outline: false,
  perPositionHeight: true,
  ...polygons,
},

10. 删除实体

static clearEntitiesLayers() {  
    const toRemove = [];  
    SuperMap3DLayer.viewer?.entities?.values.forEach((el) => {  
    if (el.pname === '删除标识') {  
        toRemove.push(el);  
    }  
    });  
    toRemove.forEach((el) => {  
        SuperMap3DLayer.viewer.entities.remove(el);  
    });  
}

11. 绘制流光墙

/**  
* 绘制流光墙  
* @param features { poly: [], childRegion: [] }  
*/  
static drawAreaWall(features) {
    // 设置墙的高度
    const hWall = [0, 23200, 8000, 2000][features.regionLevel];  
    /**  
    * 渲染墙体动画  
    * @param positions [[lng, lat], [lng, lat]]  
    */  
    const renderWall = (positions: [[lng, lat], [lng, lat]]) => {  
        SuperMap3DLayer.isRenderWall = true;  
        const arr = [];  
        positions.forEach((item) => {  
            arr.push(item[0]);  
            arr.push(item[1]);  
        });  
        const points = SuperMap3D.Cartesian3.fromDegreesArray(arr);  
        const wall = new SuperMap3D.Entity({  
            name: '立体墙效果',  
            wall: {  
                positions: points,  
                material: new SuperMap3D.PolylineTrailLinkMaterialProperty({  
                    color: SuperMap3D.Color.fromBytes(82, 193, 236).withAlpha(0.7),  
                    duration: 4500,  
                    viewer: SuperMap3DLayer.viewer,  
                }),  
                clampToGround: true,  
                minimumHeights: new Array(points.length).fill(0),  
                maximumHeights: new Array(points.length).fill(hWall),  
            },  
        });  
        SuperMap3DLayer.viewer.entities.add(wall);  
    };
    renderWall(poly)
}

11. 绘制遮罩层

static renderMask(poly:[[lng, lat], [lng, lat]]  ) {  
    if (SuperMap3DLayer.hasMask) return;  
    SuperMap3DLayer.hasMask = true;  
    const holes = [];  
    poly?.forEach((el) => {  
        const arr = [];  
        el[0].forEach((item) => {  
            arr.push(item[0]);  
            arr.push(item[1]);  
        });  
        const points = SuperMap3D.Cartesian3.fromDegreesArray(arr);  
        holes.push({  
            positions: points,  
        });  
    });  
    // 遮罩地球
    const polygonEntity = new SuperMap3D.Entity({  
        name: '遮照图层',  
        polygon: {  
            hierarchy: {  
                // 添加外部区域为1/4半圆,设置为180会报错  
                positions: SuperMap3D.Cartesian3.fromDegreesArray([0, 0, 0, 90, 179, 90, 179, 0]),  
                // 中心挖空的“洞”  
                holes,  
            },  
            material: new SuperMap3D.Color(15 / 255.0, 38 / 255.0, 84 / 255.0, 0.7),  
        },  
    });  
    SuperMap3DLayer.viewer.entities.add(polygonEntity);  
}

12. 加载模型

  static loadGlb() {
    // 加载FBX模型
    SuperMap3DLayer.viewer.entities.add({
      name: 'WYJGLTFModel',
      position: SuperMap3D.Cartesian3.fromDegrees(120.1787749, 30.48038052, 27.179),
      orientation: SuperMap3D.Quaternion.fromHeadingPitchRoll(new SuperMap3D.HeadingPitchRoll(-1.05, -0.79, -1.01)),
      model: {
        uri: '/static/wangyuanjing/wangyuanjing.gltf', // FBX模型的路径
        scale: 1.0,
        minimumPixelSize: 40,
        maximumScale: 40,
        modelMatrix: SuperMap3D.Transforms.eastNorthUpToFixedFrame(
          SuperMap3D.Cartesian3.fromDegrees(0, 0, 0),
          new SuperMap3D.Ellipsoid(6378137, 6378137, 6356752.3142451793),
          {
            rotation: {
              angle: 85,
              axis: SuperMap3D.Cartesian3.UNIT_X,
            },
          },
        ),
      },
    });
  }

四、 超图的分析工具类

已整合为一个综合类AnalysisTools,和各个不同的工具类。

image.png

const actionArr = [
  {
    name: '点选',
    iconTop: 0,
    fun: AnalysisTools.PickXYZ.bind(AnalysisTools),
  },
  {
    name: '测距',
    iconTop: -30,
    fun: AnalysisTools.MeasureDistance.bind(AnalysisTools),
  },
  {
    name: '测高',
    iconTop: -60,
    fun: AnalysisTools.MeasureHeight.bind(AnalysisTools),
  },
  {
    name: '测面积',
    iconTop: -90,
    fun: AnalysisTools.MeasureArea.bind(AnalysisTools),
  },
  {
    name: '测体积',
   iconTop: -120,
     fun: AnalysisTools.MeasureVolume.bind(AnalysisTools),
  },
  {
    name: '天际线分析',
    iconTop: -150,
    fun: AnalysisTools.DrawSkyline.bind(AnalysisTools),
  },
  {
    name: '绘制限高体',
    iconTop: -180,
    fun: AnalysisTools.DrawLimitHeight.bind(AnalysisTools),
  },
  {
    name: '视域分析',
    iconTop: -210,
    fun: AnalysisTools.DrawView3D.bind(AnalysisTools),
  },
  {
    name: '清除',
    iconTop: -240,
    fun: AnalysisTools.ClearAll.bind(AnalysisTools),
  },
];

<div
    v-for="(item, index) in actionArr"
    :key="`action-${index}`"
    class="flex cursor-pointer items-center px-10"
    @click="item.fun()"
  >
    <div class="action-icon" :style="{ backgroundPosition: `0 ${item.iconTop}px` }"></div>
    <div>{{ item.name }}</div>
</div>
  • 😄 😃 🥰 😍 😘
  • ❤️ ✨⭐ ❗❓❕❔
  • ✊✌️ ✋✋ ☝️ 👏 🤝
  • ☀️ ☔ ☁️ ❄️ ⛄ ⚡ ⛅
  • ⛪ ⛺ ⛲ ⛵ ⚓ ✈️ ⛽ ⚠️ ♨️
  • 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 0️⃣
  • ◀️ ⬇️ ▶️ ⬅️ ↙️ ↘️ ➡️ ⬆️ ↖️ ↗️
  • ⏬ ⏫ ⤵️ ⤴️ ↩️ ↪️ ↔️ ↕️ ⏪ ⏩ ℹ️
  • ♿ ㊙️ ㊗️ Ⓜ️ ⛔ ✳️ ❇️ ✴️
  • ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓ ⛎
  • ❎ ♻️ ©️ ®️ ™️ ❌ ⁉️ ⭕ ✖️ ➕ ➖ ➗ ✔️
  • ☑️ ➰ 〰️ 〽️ ▪️ ▫️ ◾ ◽ ◼️ ◻️ ⬛ ⬜ ✅ ⚫ ⚪

二、 公共点位弹泡

超图中提供的点位有时候不满足我们的点位样式需求,且难以做动画。我们可以使用自己熟悉的方式生成点位。

注意:大量数据不可使用此方法生成。性能较差。

效果图

image.png 使用方法:

  • 使用类名控制样式、图标、动画
  • callback:点击回调事件
  • dataset:额外绑定数据,在实例上可以拿到
  • 可以使用forEach循环去取数据
  • css中解决了点击无法穿透问题
import SuperPopup from '@/utils/SuperPopup';

SuperMap3DLayer.commonPopup = new SuperPopup({
  viewer: SuperMap3DLayer.viewer,
  show: options.show,
  classname: options.classname || '',
  verticalOrigin: SuperMap3D.VerticalOrigin.BOTTOM,
  position: SuperMap3D.Cartesian3.fromDegrees(options.center[0], options.center[1]),
  pixelOffset: new SuperMap3D.Cartesian2(0, 6),
  dataset: {
    id: 1,
  },
  callback: (item) => {
    console.log(item);
  },
});

封装类的样式

.g-supermap-popup {  
    position: absolute;  
    color: #fff;  
    font-size: 14px;  
    width: 128px;  
    height: 216px;  
    display: flex;
    &__1 {  
        .g-supermap-popup__content {  
            width: 44px;  
            height: 53px;  
            background: url('/static/screen/icon_point_1.png') 0 0 no-repeat;  
            position: relative;  
            z-index: 9999;  
        }  
        .g-supermap-popup__custom {  
            transform: translate(42px, 176px);  // 仅可使用translate改变图标位置
        }  
    }
    // 点击事件穿透
    &__event {  
        pointer-events: auto;  
        user-select: none;  
        cursor: pointer;  
        position: relative;  
    }
}

封装类:SuperPopup.ts

点击查看SuperPopup.ts
```typescript
let popupId = 1;

export default class SuperPopup {
  private viewerIns: any;

  private element: any;

  private html: any;

  private pixelOffset: SuperMap3D.Cartesian2;

  private position: any;

  private isShow: boolean;

  private hideOnBehindGlobe: any;

  private scaleByDistance: any;

  private translucencyByDistance: any;

  private distanceDisplayCondition: any;

  private scratch: SuperMap3D.Cartesian2;

  private RemoveCallback: () => void;

  private uid: string;

  private superPopupContainer: HTMLElement;

  private label: any;

  private dataset: any;

  private callback: () => void;

  constructor(opt) {
    this.viewerIns = opt.viewer;
    this.element = opt.element; // dom or id
    this.callback = opt.callback || null;
    this.dataset = opt.dataset;
    this.uid = `g_popup_${++popupId}`;
    this.html = opt.html;
    this.label = opt.label;
    this.position = opt.position; // Cartesian3
    this.pixelOffset = opt.pixelOffset || new SuperMap3D.Cartesian2(0, 0); // 像素偏移
    // eslint-disable-next-line no-underscore-dangle
    this.isShow = opt.show || false;
    this.hideOnBehindGlobe = opt.hideOnBehindGlobe || false; // 是否在地球背面隐藏
    this.scaleByDistance = opt.scaleByDistance; // 距离控制大小
    this.translucencyByDistance = opt.translucencyByDistance; // 距离控制透明度
    this.distanceDisplayCondition = opt.distanceDisplayCondition; // 距离控制显隐
    this.scratch = new SuperMap3D.Cartesian2();
    this.RemoveCallback = function () {};
    this.init(opt);
    this.resize();
  }

  initContainer() {
    this.superPopupContainer = document.getElementById('super_popup_container');
    if (!this.superPopupContainer) {
      this.superPopupContainer = document.createElement('div');
      this.superPopupContainer.setAttribute('id', 'super_popup_container');
      document.body.appendChild(this.superPopupContainer);
    }
  }

  init(opt) {
    // this.initContainer();
    // 在Cesium容器中添加元素
    let dom;
    // 传入html时转为dom元素
    if (this.html) {
      const parent = document.createElement('div');
      parent.innerHTML = opt.html;
      dom = parent.firstChild;
    }
    if (typeof opt.element === 'string') {
      this.element = document.getElementById(opt.element);
    } else {
      const ctx = document.createElement('div');
      ctx.setAttribute('id', this.uid);
      ctx.setAttribute('class', `g-supermap-popup ${opt.classname}`);
      const div = document.createElement('div');
      div.setAttribute('class', 'g-supermap-popup__custom');

      const innerEvent = document.createElement('div');
      innerEvent.setAttribute('class', 'g-supermap-popup__event');

      const innerDiv = document.createElement('div');
      innerDiv.setAttribute('class', 'g-supermap-popup__content');

      let labelDiv = null;
      if (this.label) {
        labelDiv = document.createElement('div');
        labelDiv.setAttribute('class', 'g-supermap-popup__label-wrap');
        labelDiv.innerText = this.label;
      }

      const triDiv = document.createElement('div');
      triDiv.setAttribute('class', 'tri');

      if (this.label) innerDiv.appendChild(labelDiv);
      div.appendChild(innerEvent);
      innerEvent.appendChild(innerDiv);
      innerEvent.appendChild(triDiv);

      ctx.appendChild(div);

      if (this.callback) {
        innerEvent.addEventListener('click', () => this.callback(this.dataset));
      }
      dom = ctx;
    }
    if (!this.element) this.element = dom; // 获取元素优先级高于获取html
    if (!this.viewerIns) {
      console.log('Popup :viewer is required!');
      return;
    }
    this.element.style.pointerEvents = 'none';
    this.element.style.transformOrigin = 'left bottom 0px'; // 缩放中心点
    this.viewerIns.container.appendChild(this.element);
    this.setViewer();
    if (this.isShow) {
      if (this.position) {
        setTimeout(() => {
          this.setPosition(opt.position);
        }, 500);
      }
    }
  }

  /**
   * 设置关联的cesium viewer
   * @param viewer
   */
  setViewer() {
    // 每一帧触发
    // this.RemoveCallback = this.viewerIns.scene.preRender.addEventListener(function () {
    //     if (this._show) {
    //         this.update()
    //     }
    // });

    // 相机改变触发
    this.RemoveCallback = this.viewerIns.camera.changed.addEventListener(() => {
      if (this.isShow) {
        this.update();
      }
    });
  }

  /**
   * 获取关联的cesium viewer
   * @return {SuperMap3D.Viewer}
   */
  getViewer() {
    return this.viewerIns;
  }

  /**
   * 设置位置
   * @param position{Array}
   */
  setPosition(position) {
    if (!position) {
      this.close();
      return;
    }
    if (!this.getViewer()) {
      return;
    }

    const canvasPosition = this.getViewer().scene.cartesianToCanvasCoordinates(position, this.scratch); // 笛卡尔坐标到画布坐标

    if (SuperMap3D.defined(canvasPosition)) {
      this.element.style.top = `${canvasPosition.y + this.pixelOffset.y - this.element.offsetHeight}px`;
      this.element.style.left = `${canvasPosition.x + this.pixelOffset.x - this.element.offsetWidth / 2}px`;
      // this.element.style.transform = `matrix(1, 0, 0, 1, ${canvasPosition.x + this.pixelOffset.x}, ${canvasPosition.y + this.pixelOffset.y})`;
      this.element.style.opacity = 1;
      this.isShow = true;
      if (
        this.hideOnBehindGlobe ||
        this.distanceDisplayCondition ||
        this.translucencyByDistance ||
        this.scaleByDistance
      ) {
        const cameraPosition = this.getViewer().camera.position;
        const distance = SuperMap3D.Cartesian3.distance(cameraPosition, position);
        if (this.hideOnBehindGlobe) {
          let { height } = this.getViewer().scene.globe.ellipsoid.cartesianToCartographic(cameraPosition);
          height += this.getViewer().scene.globe.ellipsoid.maximumRadius;
          if (!(distance > height)) {
            this.element.style.display = 'flex';
          } else {
            this.element.style.display = 'none';
          }
        }
        if (this.distanceDisplayCondition) {
          if (distance < this.distanceDisplayCondition.near || distance > this.distanceDisplayCondition.far) {
            this.element.style.opacity = 0;
            return;
          }
          this.element.style.opacity = 1;
        }
        if (this.translucencyByDistance) {
          if (distance < this.translucencyByDistance.near) {
            this.element.style.opacity = this.translucencyByDistance.nearValue;
          } else if (distance > this.translucencyByDistance.far) {
            this.element.style.opacity = this.translucencyByDistance.farValue;
          } else {
            const val1 = this.translucencyByDistance.farValue - this.translucencyByDistance.nearValue;
            const val2 = this.translucencyByDistance.far - this.translucencyByDistance.near;
            const val3 =
              ((distance - this.translucencyByDistance.near) / val2) * val1 + this.translucencyByDistance.nearValue;
            this.element.style.opacity = val3;
          }
        }
        if (this.scaleByDistance) {
          if (distance < this.scaleByDistance.near) {
            const val = this.scaleByDistance.nearValue;
            this.element.style.transform = `scale(${val}, ${val})`;
          } else if (distance > this.scaleByDistance.far) {
            const val = this.scaleByDistance.farValue;
            this.element.style.transform = `scale(${val}, ${val})`;
          } else {
            const val1 = this.scaleByDistance.farValue - this.scaleByDistance.nearValue;
            const val2 = this.scaleByDistance.far - this.scaleByDistance.near;
            const val3 = ((distance - this.scaleByDistance.near) / val2) * val1 + this.scaleByDistance.nearValue;
            this.element.style.transform = `scale(${val3}, ${val3})`;
          }
        }
      }
    }
    this.position = position;
  }

  // 当窗口大小改变时候重新计算popup的位置
  resize() {
    window.addEventListener('resize', () => {
      this.setPosition(this.position);
    });
  }

  update() {
    this.setPosition(this.position);
  }

  getPosition() {
    return this.position;
  }

  close() {
    this.element.style.opacity = 0;
    this.isShow = false;
  }

  show() {
    if (this.position) {
      this.element.style.opacity = 1;
      this.isShow = true;
      this.setPosition(this.position);
    }
  }

  hide() {
    if (this.position) {
      this.element.style.opacity = 0;
      this.isShow = false;
      this.setPosition(this.position);
    }
  }

  destroy() {
    this.RemoveCallback();
    this.close();
    this.viewerIns.container?.removeChild(this.element);
  }
}
```