javascriptentitycesiumjscartesianwgs84

Place an HTML element on a Cesium Entity's position


I need to define the location of an html element based on the location of a Cesium entity. I've used the mouse position (Cesium.Cartesian3.clone(movement.endPosition)), which is in window coordinates, as a test and it works. So I need to get the entity position, transform it to the WGS84 coordinates, transform those to the window coordinates and use them for element.style.left = window_coord.x and element.style.top = window_coord.y.
So I get the entity.position and the x, y and z values are right. However when I want to convert them to WGS84 coordinates something goes wrong and I get NaN for lat, lon and height.

These are the variations I've tried, both result in NaN for lat, lon and height:

var carto = Cesium.Ellipsoid.WGS84.cartesianToCartographic(entity.position);
or
var carto = Cesium.Cartographic.fromCartesian(entity.position);
or
var ellipsoid = viewer.scene.globe.ellipsoid;
var cartesian = viewer.camera.pickEllipsoid(entity.position, ellipsoid);
var carto = ellipsoid.cartesianToCartographic(cartesian);


var entity_pos = Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, carto);
element.style.left = entity_pos.x;
element.style.top = entity_pos.y;

Console log showing entity.position, carto and entity_pos

My other idea was manually calculating the WGS84 coordinates but this would be such a dumb workaround. Any ideas on how I could do this without influencing the user experience.


Solution

  • Getting the entity's position must be done with the entity.position.getValue(clock.currentTime) function, because entities can be moving objects with changing positions.

    Here's a demo of an HTML element following an entity position.

    // Create Cesium Viewer
    var viewer = new Cesium.Viewer('cesiumContainer');
    
    // Create a sample HTML element in the document.
    var testElement = document.createElement('div');
    testElement.style.position = 'absolute';
    testElement.style.border = '2px solid #444';
    testElement.style.font = '16px sans-serif';
    testElement.innerHTML = 'Testing';
    viewer.container.appendChild(testElement);
    var isEntityVisible = true;
    
    // Create a sample entity
    var entity = viewer.entities.add({
        position : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
        point : {
            pixelSize : 10,
            color : Cesium.Color.YELLOW
        }
    });
    
    // Pre-allocate memory once.  Don't re-allocate for each animation frame.
    var scratch3dPosition = new Cesium.Cartesian3();
    var scratch2dPosition = new Cesium.Cartesian2();
    
    // Every animation frame, update the HTML element position from the entity.
    viewer.clock.onTick.addEventListener(function(clock) {
        var position3d;
        var position2d;
    
        // Not all entities have a position, need to check.
        if (entity.position) {
            position3d = entity.position.getValue(clock.currentTime, scratch3dPosition);
        }
    
        // Moving entities don't have a position for every possible time, need to check.
        if (position3d) {
            position2d = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
                viewer.scene, position3d, scratch2dPosition);
        }
    
        // Having a position doesn't guarantee it's on screen, need to check.
        if (position2d) {
            // Set the HTML position to match the entity's position.
            testElement.style.left = position2d.x + 'px';
            testElement.style.top = position2d.y + 'px';
    
            // Reveal HTML when entity comes on screen
            if (!isEntityVisible) {
                isEntityVisible = true;
                testElement.style.display = 'block';
            }
        } else if (isEntityVisible) {
            // Hide HTML when entity goes off screen or loses its position.
            isEntityVisible = false;
            testElement.style.display = 'none';
        }
    });