preface

Two days before his contact information is added in the material, after some friend and I discuss about the effect of Cesium and functional implementation, there are a lot of people asked this text markup is how to implement, one by one answer laborious, simply write an article to implement my ideas are offered, and simple class encapsulation, something for everyone.

The basic idea

Essentially, you provide a Cartesian coordinate and render the DOM element into the Cesium container for rendering. PostRender is used to update positions in real time to ensure that they are consistent with Cartesian coordinates. The idea is pretty simple, so let’s implement it.

Basic implementation

The following section will improve the method of marking points step by step. If you want to see the finished product directly, you can skip to the end of the method implementation.

Parameter setting

First of all, the most basic is the position coordinate, title mark content and id unique identifier. Secondly, we can add some parameters such as height mark height, hidden height, etc., can be added according to your own needs.

Method implementation

Again, first we need to generate a sphere for our labeled container.

viewer = new Cesium.Viewer("cesiumContainer"{});Copy the code

Then we can build the basic method of adding markers and set the parameters that we want to configure. I’ve only set the basic parameters here.

addDynamicLabel({ position, title = "Text tag", id = "0" }) {
	/ /... Do SomeThing
},
Copy the code

We need to dynamically create a div as a marker point, style and content it, and add it to the container where the sphere resides

addDynamicLabel({ position, title = "Text tag", id = "0" }) {
	let div = document.createElement("div");
    div.id = id;
    div.style.position = "absolute";
    div.style.width = "100px";
    div.style.height = "30px";
    let divHTML = ` <div style="width:100px; height:30px; Background: rgba (255122,0,0.4) ">${title}</div>
    `;
    div.innerHTML = divHTML;
    viewer.cesiumWidget.container.appendChild(div);
},	
Copy the code

Now that we have placed the tag on the page, although it is absolutely positioned, it does not have the corresponding top and left values to display it where we want it to appear. The next thing we need to do is convert the coordinates passed in to the screen x and y as the offset of the marker. Cesium provides a Cesium. SceneTransforms. Wgs84ToWindowCoordinates () method for us to transform.

Cesium. SceneTransforms. Wgs84ToWindowCoordinates – Cartesian2 (scene, the position, the result)

Convert positions in WGS84 coordinates to window coordinates. This is typically used to place HTML elements in the same screen position as objects in the scene.

parameter type describe
scene Scene The scene.
position Cartesian3 Position in WGS84 (world) coordinates.
result Cartesian2 optionalReturns an optional object for the input position converted to window coordinates.
addDynamicLabel({ position, title = "Text tag", id = "0" }) {
	let div = document.createElement("div");
    div.id = id;
    div.style.position = "absolute";
    div.style.width = "100px";
    div.style.height = "30px";
    let divHTML = ` <div style="width:100px; height:30px; Background: rgba (255122,0,0.4) ">${title}</div>
    `;
    div.innerHTML = divHTML;
    viewer.cesiumWidget.container.appendChild(div);
    
    let vmPosition = Cesium.Cartesian3.fromDegrees(
        position[0],
        position[1].500
    );
    const canvasHeight = viewer.scene.canvas.height;
    const windowPosition = new Cesium.Cartesian2();
    Cesium.SceneTransforms.wgs84ToWindowCoordinates(
        viewer.scene,
        vmPosition,
        windowPosition
    );
    div.style.bottom = canvasHeight - windowPosition.y + "px";
    const elWidth = div.offsetWidth;
    div.style.left = windowPosition.x - elWidth / 2 + "px";
},	
Copy the code

Now our marker is in the correct position, but if we drag or zoom in and out, we’ll see that the marker is not moving! This is obviously not true; the marker should change position as the sphere moves and shrinks. Cesiumwidget.js creates a timer that is used to refresh and render pages. The timer is used to refresh and render pages. (Actually because I didn’t read the source code carefully). After looking at Cesium’s mechanics, we know that in order for the tag to follow the sphere, we need to have the tag constantly refresh the render as well. Cesium is nice enough to give us scene.postRender, get the events at the end of each frame of the current scene and listen for the events to trigger at the end of each frame. We can get the event of each frame just by listening for it.

addDynamicLabel({ position, title = "Text tag", id = "0" }) {
    let div = document.createElement("div");
    div.id = id;
    div.style.position = "absolute";
    div.style.width = "100px";
    div.style.height = "30px";
    let divHTML = ` <div style="width:100px; height:30px; Background: rgba (255122,0,0.4) ">${title}</div>
`;
    div.innerHTML = divHTML;
    viewer.cesiumWidget.container.appendChild(div);
    let vmPosition = Cesium.Cartesian3.fromDegrees(
        position[0],
        position[1].500
    );
    viewer.scene.postRender.addEventListener(() = > {
        const canvasHeight = viewer.scene.canvas.height;
        const windowPosition = new Cesium.Cartesian2();
        Cesium.SceneTransforms.wgs84ToWindowCoordinates(
            viewer.scene,
            vmPosition,
            windowPosition
        );
        div.style.bottom = canvasHeight - windowPosition.y + "px";
        const elWidth = div.offsetWidth;
        div.style.left = windowPosition.x - elWidth / 2 + "px";
    }, this);
},
Copy the code

Once the method is built, all we need to do is call it and see that our marker has appeared on the sphere.

let position = [121.54035.38.92146],
    title = "Mark of MOE",
    id = 210204121;
this.addDynamicLabel({ position, title, id });
Copy the code

At this point, we’ve actually done the basic functionality, and we can make the styles of the tags cool by ourselves, like the GIFs I started with.

Wrapper class

This tag is likely to be used in many places in your business, and the best way to do this is to encapsulate it as a plug-and-play file. Let’s try to separate this functionality out and wrap it into a class. Here we use es6 class encapsulation. Because at the beginning OF my own to explore the packaging of a lot of detour, so I want to package class here also briefly, you can do other functions in the future.

/ * * *@descripion:
 * @param {Viewer} viewer
 * @param {Cartesian2} position
 * @param {String} title
 * @param {String} id
 * @param {boolean} IsHighHidden Indicates whether it is highly hidden *@return {*}* /
export default class DynamicLabel {
    constructor({viewer , position , height , title , id , isHighHidden = true}) {
        this.viewer = viewer;
        this.height = height;
        this.isHighHidden = isHighHidden;
        this.position = Cesium.Cartesian3.fromDegrees(
          position[0],
          position[1],
          height
        );
        this.addLabel();
    }
    addLabel() {
        // ...
    }
    addPostRender() {
        // ...
    }
    postRender() {
        // ...}}Copy the code

Normally we’d be done, but the way we load the DOM is too crude and unmaintainable to fill the DOM element with innerHTML or make changes to the content and style within the string every time. At this point we can use the vue. extend + $mount constructor to create a “subclass”. This method is also I read other people’s open source code after suddenly enlightened, did not expect to be able to use so! Long knowledge.

We first create a vue file label.vue and put our label styles and stuff in it:

<template> <div :id="id" class="divlabel-container" v-if="show" > <div class="animate-maker-border"> <span class="animate-marker__text">{{ title }}</span> </div> </div> </template> <script> export default { name: "DynamicLabel", data() { return { show: true, }; }, props: {title: {type:String, default: "title ",}, id:{type:String, default:'001'}},}; </script> <style lang="scss"> .divlabel-container { position: absolute; left: 0; bottom: 0; pointer-events: none; cursor: pointer; } .animate-maker-border { width: 150px; height: 30px; margin: 0; color: #f7ea00; Box-shadow: inset 0 0 0 1px rgba(247, 234, 0, 0.56); } .animate-marker__text { color: #fff; font-size: 14px; display: flex; width: 100%; height: 100%; align-items: center; justify-content: center; font-weight: bolder; user-select: none; cursor: pointer; Background: RGBA (0, 173, 181, 0.32); } </style>Copy the code

Next, transform the DynamicLabel we encapsulate

/ * * *@descripion:
 * @param {Viewer} viewer
 * @param {Cartesian2} position
 * @param {String} title
 * @param {String} id
 * @param {boolean} IsHighHidden Indicates whether it is highly hidden *@return {*}* /

import Vue from "vue";
import Label from "./label.vue";
let WindowVm = Vue.extend(Label);
export default class DynamicLabel {
    constructor({viewer , position , height , title , id , isHighHidden = true}) {
        this.viewer = viewer;
        this.height = height;
        this.isHighHidden = isHighHidden;
        this.position = Cesium.Cartesian3.fromDegrees(
          position[0],
          position[1],
          height
        );
        
        this.vmInstance = new WindowVm({
          propsData: {
            title,
            id
          }
        }).$mount(); // Create a panel based on the template
        viewer.cesiumWidget.container.appendChild(this.vmInstance.$el); // Add content generated by the string template to the DOM
    	this.addPostRender();
    }
    
  // Add scene events
  addPostRender() {
    this.viewer.scene.postRender.addEventListener(this.postRender, this);
  }

  // The location of the scene rendering event update window is consistent with the Cartesian coordinates
  postRender() {
    if (!this.vmInstance.$el || !this.vmInstance.$el.style) return;
    const canvasHeight = this.viewer.scene.canvas.height;
    const windowPosition = new Cesium.Cartesian2();
    Cesium.SceneTransforms.wgs84ToWindowCoordinates(
      this.viewer.scene,
      this.position,
      windowPosition
    );
    this.vmInstance.$el.style.bottom =
      canvasHeight - windowPosition.y + this.height + "px";
    const elWidth = this.vmInstance.$el.offsetWidth;
    this.vmInstance.$el.style.left = windowPosition.x - elWidth / 2 + "px";
    if (
      this.viewer.camera.positionCartographic.height > 4000 &&
      this.isHighHidden
    ) {
      this.vmInstance.$el.style.display = "none";
    } else {
      this.vmInstance.$el.style.display = "block"; }}}Copy the code

Finished, now see this class is not very refreshing, on vue. extend related principles and operations HERE I do not explain, we can baidu. In my scene rendering method by enclosing viewer. Camera. PositionCartographic. Height added a judgment, according to the current camera height decides if the tag is to let this plugin is more flexible.

In the end, we just need to import and call in our page file, and we can have the same effect as the previous method implementation

import DynamicLabel from './plugins/DynamicLabel'

addDynamicLabel() {
    let label = new DynamicLabel({
        viewer, 
        position: [121.54035.38.92146].height:500.title:'MOE label'.id:'210201025'})}Copy the code

The last

In this article we provide an idea for implementing and encapsulating specified functions in Cesium scenes with a simple text marker. In fact, many functions can be encapsulated in this way, such as custom pop-ups, radar scanning effects, grid rendering from the previous article, flying along, etc. As long as we get the basic idea straight, it’s actually not difficult. Or, as I did, I could implement the functionality by means first and then encapsulate and expose it step by step. If you find it helpful, you can click 👍 to follow the column, and I will include Cesium and GIS related articles.

The appendix

series

Realizing smart City based on Vue+Cesium+Supermap (I)

Realizing smart City based on Vue+Cesium+Supermap (II)

【 3D GIS Visualization 】 Realizing smart city based on Vue+Cesium+Supermap

Realizing smart City based on Vue+Cesium+Supermap (IV)