Measuring Height

Introducing the process of measuring and outputting elevation and relative altitude within the map. This page discusses the process of changing the mouse mode to height measurement mode in the map and visualizing the measurement results using JSPoint.

Elevation and Ground Altitude

Height is commonly measured as elevation above sea level and ground altitude.

  • Elevation: Height measured from the sea level.

  • Ground Altitude: Height measured from the ground surface.

In this sample code, when the ground is clicked, the elevation value is displayed, and when a building or other facility is clicked, both the elevation and ground altitude values are displayed together.

Here is the complete code implementing the above functionality.
//* Initialization function to run after engine load (Module.postRun) */
function init() {
    // Call engine initialization API (mandatory)
    Module.Start(window.innerWidth, window.innerHeight);

    // Set camera location
    Module.getViewCamera().setLocation(new Module.JSVector3D(126.92836647767662, 37.52439503321471, 1000.0));

    // Create symbol for icon management
    GLOBAL.Symbol = Module.getSymbol();

    // Create analysis output POI layer
    var layerList = new Module.JSLayerList(true);
    GLOBAL.Layer = layerList.createLayer("MEASURE_POI", Module.ELT_3DPOINT);
    GLOBAL.Layer.setMaxDistance(20000.0);
    GLOBAL.Layer.setSelectable(false);

    Module.XDEMapCreateLayer("facility_build", "http://xdworld.vworld.kr:8080", 8080, true, true, false, 9, 0, 15);

    initEvent(Module.canvas);
}

var GLOBAL = {
    Symbol: null, // Icon management symbol object
    Layer: null, // Layer for storing POI
    nIndex: 0, // Index for creating POI, Icon
};

/* Set up events */
function initEvent(canvas) {
    // Set event for distance measurement
    canvas.addEventListener("Fire_EventAddAltitudePoint", function (e) {
        createPOI(new Module.JSVector3D(e.dLon, e.dLat, e.dAlt), "rgba(10, 10, 0, 0.5)", e.dGroundAltitude, e.dObjectAltitude);
    });
}

/* Change mouse state */
function setMouseState(_option) {
    // Set mouse mode
    Module.XDSetMouseState(_option);
}

/* Create POI for displaying analysis content */
function createPOI(_position, _color, _value, _subValue) {
    // Create a canvas to draw the POI icon image
    var drawCanvas = document.createElement("canvas");
    drawCanvas.width = 200;
    drawCanvas.height = 100;

    // Return icon image data
    var imageData = drawIcon(drawCanvas, _color, _value, _subValue),
        nIndex = GLOBAL.nIndex;
    // Register icon image in symbol
    if (GLOBAL.Symbol.insertIcon("Icon" + nIndex, imageData, drawCanvas.width, drawCanvas.height)) {
        // Return registered icon object
        var icon = GLOBAL.Symbol.getIcon("Icon" + nIndex);

        // Create JSPoint object
        var count = GLOBAL.Layer.getObjectCount(),
            poi = Module.createPoint("POI" + nIndex);
        poi.setPosition(_position); // Set location
        poi.setIcon(icon); // Set icon

        // Add object to layer
        GLOBAL.Layer.addObject(poi, 0);

        // Increment index value
        GLOBAL.nIndex++;
    }
}

/* Return icon image data */
function drawIcon(_canvas, _color, _value, _subValue) {
    // Return context and clear background
    var ctx = _canvas.getContext("2d"),
        width = _canvas.width,
        height = _canvas.height;
    ctx.clearRect(0, 0, width, height);

    // Draw background and height value text
    if (_subValue == -1) {
        drawRoundRect(ctx, 50, 20, 100, 20, 5, _color); // If object height value is not valid
    } else {
        drawRoundRect(ctx, 50, 5, 100, 35, 5, _color); // If object height value is valid
        setText(ctx, width * 0.5, height * 0.2, "Ground Altitude: " + setKilloUnit(_subValue, 0.001, 0));
    }
    setText(ctx, width * 0.5, height * 0.2 + 15, "Elevation: " + setKilloUnit(_value, 0.001, 0));

    // Draw location indicator dot
    drawDot(ctx, width, height);

    return ctx.getImageData(0, 0, _canvas.width, _canvas.height).data;
}

/* Draw location indicator dot */
function drawDot(ctx, width, height) {
    ctx.beginPath();
    ctx.lineWidth = 6;
    ctx.arc(width * 0.5, height * 0.5, 2, 0, 2 * Math.PI, false);
    ctx.closePath();

    ctx.fillStyle = "rgba(255, 0, 0, 0.8)";
    ctx.fill();
    ctx.lineWidth = 8;
    ctx.strokeStyle = "rgba(255, 255, 0, 0.8)";
    ctx.stroke();
}

/* Draw rounded rectangle background */
function drawRoundRect(ctx, x, y, width, height, radius, color) {
    if (width < 2 * radius) {
        radius = width * 0.5;
    }
    if (height < 2 * radius) {
        radius = height * 0.5;
    }

    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.arcTo(x + width, y, x + width, y + height, radius);
    ctx.arcTo(x + width, y + height, x, y + height, radius);
    ctx.arcTo(x, y + height, x, y, radius);
    ctx.arcTo(x, y, x + width, y, radius);
    ctx.closePath();

    // Draw rectangle
    ctx.fillStyle = color;
    ctx.fill();

    return ctx;
}

/* Draw text */
function setText(_ctx, _posX, _posY, _strText) {
    _ctx.font = "bold 12px sans-serif";
    _ctx.textAlign = "center";

    _ctx.fillStyle = "rgb(255, 255, 255)";
    _ctx.fillText(_strText, _posX, _posY);
}

/* Convert to m/km text */
function setKilloUnit(_text, _meterToKilloRate, _decimalSize) {
    if (_decimalSize < 0) {
        _decimalSize = 0;
    }
    if (typeof _text == "number") {
        if (_text < 1.0 / (_meterToKilloRate * Math.pow(10, _decimalSize))) {
            _text = _text.toFixed(1).toString() + "m";
        } else {
            _text = (_text * _meterToKilloRate).toFixed(2).toString() + "㎞";
        }
    }
    return _text;
}

/* Reset analysis content */
function clearAnalysis() {
    var layer = GLOBAL.Layer,
        symbol = GLOBAL.Symbol;
    if (layer == null) {
        return;
    }

    // Delete registered icon list
    var i, len, icon, poi;
    for (i = 0, len = layer.getObjectCount(); i < len; i++) {
        poi = layer.keyAtObject("POI" + i);
        icon = poi.getIcon();

        // Delete POI referencing icon
        layer.removeAtKey("POI" + i);

        // Delete icon from symbol
        symbol.deleteIcon(icon.getId());
    }

    // Reset POI, Icon key index
    GLOBAL.nIndex = 0;
}

Following are the detailed steps of the process.

Global Variables

Before implementing the functionality, global variables are declared.

var GLOBAL = {
    Symbol: null, // Icon management symbol object
    Layer: null, // Layer for storing POI
    nIndex: 0, // Index for creating POI, Icon
};

Symbol

The texture map object that stores image textures is stored here.

For a brief description of JSSymbol, refer to the explanation of JSSymbol in the step 5. Creating Distance Object section of the distance measurement tutorial.

Layer

The layer where the JSPoint object will be stored is saved in this variable.

The process of saving the layer is described in the step 1. Creating Layer step.

Implementing the Functionality

step 1. Creating Layer

A layer is created to visualize the height measurement icon and height values.

For a description of layer types, see here.

var layerList = new Module.JSLayerList(true);

GLOBAL.Layer = layerList.createLayer("MEASURE_POI", Module.ELT_3DPOINT);
GLOBAL.Layer.setMaxDistance(20000.0);
GLOBAL.Layer.setSelectable(false);

step 2. Registering Events

Events are registered to receive the calculated height from the engine.

function initEvent(canvas) {
    canvas.addEventListener("Fire_EventAddAltitudePoint", function (e) {
        createPOI(new Module.JSVector3D(e.dLon, e.dLat, e.dAlt), "rgba(10, 10, 0, 0.5)", e.dGroundAltitude, e.dObjectAltitude);
    });
}

The Fire_EventAddAltitudePoint event occurs when the mouse mode is set to MML_ANALYS_ALTITUDE.

The process of setting the mouse mode is described in the next step, step 3. Changing Mouse Mode.

After changing the mouse mode and clicking on the map, the height information is returned as event parameters.

  • dLon: Longitude of the height measurement location

  • dLat: Latitude of the height measurement location

  • dAlt: Altitude of the height measurement location

  • dGroundAltitude: Elevation value. If the ground is clicked, this value will be the same as dAlt.

  • dObjectAltitude: Ground altitude value

Based on this event information, the createPOI function creates a JSPoint object to display the altitude information.

The following process describes various canvas drawing functions before creating the JSPoint object.

To directly proceed to the step of creating the JSPoint object, go here.

step 3. Changing Mouse Mode

The mouse mode is changed for height measurement.

For a description of mouse modes, see here.

Module.XDSetMouseState(Module.MML_ANALYS_ALTITUDE);

step 4 - 1. Creating Height Icon

The function below creates an icon to render the received height value.

Executing the function will create the following image on the canvas.

function drawIcon(_canvas, _color, _value, _subValue) {
    // Return context and clear background
    var ctx = _canvas.getContext("2d"),
        width = _canvas.width,
        height = _canvas.height;
    ctx.clearRect(0, 0, width, height);

    // Draw background and height value text
    if (_subValue == -1) {
        drawRoundRect(ctx, 50, 20, 100, 20, 5, _color); // If object height value is not valid
    } else {
        drawRoundRect(ctx, 50, 5, 100, 35, 5, _color); // If object height value is valid
        setText(ctx, width * 0.5, height * 0.2, "Ground Altitude: " + setKilloUnit(_subValue, 0.001, 0));
    }
    setText(ctx, width * 0.5, height * 0.2 + 15, "Elevation: " + setKilloUnit(_value, 0.001, 0));

    // Draw location indicator dot
    drawDot(ctx, width, height);

    return ctx.getImageData(0, 0, _canvas.width, _canvas.height).data;
}

First, a canvas is created to draw the image, then a background for the text is painted (step 4-3. Creating Height Rectangle Icon),

Based on the returned altitude values, text is entered. (step 4-4. Creating Height Measurement Result Icon)

Finally, a location indicator dot is drawn to mark the clicked location. (step 4-2. Creating Height Point Icon)

step 4 - 2. Creating Height Point Icon

A Point Icon is created to visualize the measured height location.

function drawDot(ctx, width, height) {
    ctx.beginPath();
    ctx.lineWidth = 6;
    ctx.arc(width * 0.5, height * 0.5, 2, 0, 2 * Math.PI, false);
    ctx.closePath();

    ctx.fillStyle = "rgba(255, 0, 0, 0.8)";
    ctx.fill();
    ctx.lineWidth = 8;
    ctx.strokeStyle = "rgba(255, 255, 0, 0.8)";
    ctx.stroke();
}

step 4 - 3. Creating Height Rectangle Icon

A rectangle is drawn as a background to visualize the received height value.

function drawRoundRect(ctx, x, y, width, height, radius, color) {
    if (width < 2 * radius) {
        radius = width * 0.5;
    }

    if (height < 2 * radius) {
        radius = height * 0.5;
    }

    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.arcTo(x + width, y, x + width, y + height, radius);
    ctx.arcTo(x + width, y + height, x, y + height, radius);
    ctx.arcTo(x, y + height, x, y, radius);
    ctx.arcTo(x, y, x + width, y, radius);
    ctx.closePath();

    // Draw rectangle
    ctx.fillStyle = color;
    ctx.fill();

    return ctx;
}

step 4 - 4. Creating Height Measurement Result Icon

The received height value is written as text on the rectangle icon.

function setText(_ctx, _posX, _posY, _strText) {
    _ctx.font = "bold 12px sans-serif";
    _ctx.textAlign = "center";

    _ctx.fillStyle = "rgb(255, 255, 255)";
    _ctx.fillText(_strText, _posX, _posY);
}

step 4 - 5. Converting Height Measurement to m/km Text

If necessary, the received height value is converted to m/km text.

function setKilloUnit(_text, _meterToKilloRate, _decimalSize) {
    if (_decimalSize < 0) {
        _decimalSize = 0;
    }
    if (typeof _text == "number") {
        if (_text < 1.0 / (_meterToKilloRate * Math.pow(10, _decimalSize))) {
            _text = _text.toFixed(1).toString() + "m";
        } else {
            _text = (_text * _meterToKilloRate).toFixed(2).toString() + "㎞";
        }
    }
    return _text;
}

step 5. Creating Height Object

An object is created with the created icon and added to the layer.

function createPOI(_position, _color, _value, _subValue) {
    // Create a canvas to draw the POI icon image
    var drawCanvas = document.createElement("canvas");
    drawCanvas.width = 200;
    drawCanvas.height = 100;

    // Return icon image data
    var imageData = drawIcon(drawCanvas, _color, _value, _subValue),
        nIndex = GLOBAL.nIndex;
    // Register icon image in symbol
    if (GLOBAL.Symbol.insertIcon("Icon" + nIndex, imageData, drawCanvas.width, drawCanvas.height)) {
        // Return registered icon object
        var icon = GLOBAL.Symbol.getIcon("Icon" + nIndex);

        // Create JSPoint object
        var count = GLOBAL.Layer.getObjectCount(),
            poi = Module.createPoint("POI" + nIndex);
        poi.setPosition(_position); // Set location
        poi.setIcon(icon); // Set icon

        // Add object to layer
        GLOBAL.Layer.addObject(poi, 0);

        // Increment index value
        GLOBAL.nIndex++;
    }
}

step 6. Resetting Height Measurement

Height measurement results and objects are reset.

function clearAnalysis() {
    var layer = GLOBAL.Layer,
        symbol = GLOBAL.Symbol;

    if (layer == null) {
        return;
    }

    // Delete registered icon list
    var i, len, icon, poi;
    for (i = 0, len = layer.getObjectCount(); i < len; i++) {
        poi = layer.keyAtObject("POI" + i);
        icon = poi.getIcon();

        // Delete POI referencing icon
        layer.removeAtKey("POI" + i);

        // Delete icon from symbol
        symbol.deleteIcon(icon.getId());
    }

    // Reset POI, Icon key index
    GLOBAL.nIndex = 0;
}

Result Screen

After completing all the steps, you will have a measurement feature that displays height information at the clicked location.

If you want to check the live code for the height measurement process, click here.

Last updated