Creating a Custom TEF Application
You can create custom TerraExplorer Fusion applications by using iframes to embed the TEF window in your custom HTML. The TerraExplorer and Fusion APIs, can then be used together with any other script to customize your application. See "Adding Custom TEF Tools" in this chapter for information.

To create a custom TEF application:
1. Copy the entire .\TerraExplorerFusion directory to your server and place it next to your web application.
2. In your HTML file, create the TEF iframe element and link it to the TE.html page:
<iframe id='tef' src="./TEF/TE.html" class="tef"/>
3. Add custom URL parameters to control different TerraExplorer Fusion characteristics such as the project to load, a custom styling file (css) to hide/restyle GUI elements, start location etc. See "URL Parameters" in this chapter for information.
<iframe id='tef' src="./TEF/TE.html?project=https://cloud.skylineglobe.com/demos/projects/Mexico_Beach_Emergency_Response&css=sampleCustomizedGUI" class="tef"/>
4. Add the onTEFInit callback function that will be called when TEF initializes. In this callback you can get the TerraExplorer main API interface, SGWorld, and use it to add and control elements in the 3D view:
function onTEFInit(event) {
SGWorld = document.getElementById('tef').contentWindow.SGWorld;
}
5. Add the onTEFProjectLoaded callback function that will be called when the project is loaded:
function onTEFProjectLoaded(event) {
var position= SGWorld.Creator.CreatePosition (-97.0, 38.0, 100, 0, -70,0,500);
SGWorld.Navigate.FlyTo (position);
}
Note A sample custom TEF application file called sampleTEinIFrame.html is included in the application files of TerraExplorer Fusion under custom (.\custom\sampleTEinIFrame.html). This demo initializes the TEF and offers three options: Load with GUI, Load without GUI, and Load with Customized GUI.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="../js/jquery-3.7.1.min.js?v=8.2.0.50312-qqjmsw038s"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N"
crossorigin="anonymous"></script>
<script language="javascript" src="../Tools/ToolsCommon80.js?v=8.2.0.50312-qqjmsw038s"></script>
<style>
body {
width: 100%;
height: 100%;
background-color: #DCDCDC;
}
.leftBar {
background-color: beige;
min-height: 80vh;
}
.tef {
width: 100%;
height: 80vh;
}
.tefBorder{
border:0.2px solid white;
}
button {
width: 40%;
height: 60px;
padding: 0 !important;
}
div.lead {
margin-left: 10px;
}
.caption{
margin: 0px auto;
width: 35%;
text-align: center;
}
.disable-scroll{
position: fixed;
}
.show-scroll{
overflow-y:scroll;
}
</style>
<script>
//A ref to SGWorld API Object.
var SGWorld = null;
//A ref to the TEF IFrame element.
var TEF = null;
$(() => {
TEF = document.getElementById('tef');
});
/**
* Redirects the current page to the specified URL. This function is responsible for redirecting the page to another URL based on the selected SSO Identity Provider (IdP).
*/
functionloginRedirectHelper(url) {
location = url;
}
/**
* Called by TEF on init.
*/
function onTEFInit(event) {
//Gets the SGWorld object from the contentWindow of the IFrame.
SGWorld = TEF.contentWindow.SGWorld;
//Attaches an event handler to show the frame rate (FPS).
SGWorld.AttachEvent('OnRenderQualityChanged', OnRenderQualityChanged);
}
/**
* Called by TEF once the project is loaded.
*/
functiononTEFProjectLoaded(event) {
$("#analysisButtons").show();
}
function loadTEF(GUI) {
AddIFrameBorder();
var guiURL = (GUI) ? "" : "&script=sampleCustomButtons.js&css=sampleCustomButtons";
/**
* Retrieves the 'loginSGSError' and 'loginSGSURL' parameters from the URL.
*/
var loginErrorURL = GetParamValue('loginSGSError', '') == "" ? "" : `&loginSGSError=${GetParamValue('loginSGSError', '')}`;
var loginSGSURL = GetParamValue('loginSGSURL', '') == "" ? "" : `&loginSGSURL=${GetParamValue('loginSGSURL', '')}`;
removeURLParameter('loginSGSError');
removeURLParameter('loginSGSURL');
$("#tef").attr('src', "../TE.html?project=https://cloud.skylineglobe.com/Skyline/projects/TEW_Demo_Project.726595" + guiURL + loginErrorURL + loginSGSURL);
DisablePageScrollOnFusionIFrameFocus();
}
function loadPartialTEF() {
AddIFrameBorder();
var guiURL = "&css=sampleCustomizedGUI";
/**
* Retrieves the 'loginSGSError' and 'loginSGSURL' parameters from the URL.
*/
var loginErrorURL = GetParamValue('loginSGSError', '') == "" ? "" : `&loginSGSError=${GetParamValue('loginSGSError', '')}`;
var loginSGSURL = GetParamValue('loginSGSURL', '') == "" ? "" : `&loginSGSURL=${GetParamValue('loginSGSURL', '')}`;
removeURLParameter('loginSGSError');
removeURLParameter('loginSGSURL');
$("#tef").attr('src', "../TE.html?project=https://cloud.skylineglobe.com/Skyline/projects/TEW_Demo_Project.726595" + guiURL + loginErrorURL + loginSGSURL);
DisablePageScrollOnFusionIFrameFocus();
}
functionAddIFrameBorder(){
$("#tef").addClass("tefBorder");
}
//Prevents page scroll on Fusion zoom/camera movement (on mouse wheel and on arrow up & down keys).
functionDisablePageScrollOnFusionIFrameFocus(){
$("#tef").on("mouseover", () => {
if($("body").height() > $(window).height())
$('body').addClass("show-scroll");
$('body').addClass("disable-scroll");
});
$("#tef").on("mouseleave", () => {
$('body').removeClass("show-scroll");
$('body').removeClass("disable-scroll");
})
}
function rotate() {
if (SGWorld === null)
alert("TEF not loaded");
else
SGWorld.Command.Execute(1057, 0);
}
function zoomIn() {
if (SGWorld === null)
alert("TEF not loaded");
else
SGWorld.Navigate.ZoomIn();
}
function distance() {
if (SGWorld === null)
alert("TEF not loaded");
else
TEF.contentWindow.analysis.openAnalysisToolURL({
name:'distanceMeasurement',
url:'./Tools/DistanceMeasurement/distanceMeasurement.html',
title:"Distance Measurement"
})
}
function area() {
if (SGWorld === null)
alert("TEF not loaded");
else
TEF.contentWindow.analysis.openAnalysisToolURL({
name:'areaMeasurement',
url:'./Tools/AreaMeasurement/AreaMeasurement.html',
title:'Area Measurement'
})
}
function volume() {
if (SGWorld === null)
alert("TEF not loaded");
else
TEF.contentWindow.analysis.openAnalysisToolURL({
name:'volume',
url:'./Tools/Volume/Volume.html',
title:'Volume Measurement'
})
}
function profile() {
if (SGWorld === null)
alert("TEF not loaded");
else
TEF.contentWindow.analysis.openAnalysisToolURL({
name:'profile',
url:'./Tools/TerrainProfile/TerrainProfile.html',
title:'Model Elevation Profile',
})
}
function comparison() {
if (SGWorld === null)
alert("TEF not loaded");
else
TEF.contentWindow.analysis.openAnalysisToolURL({
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sgs-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
spec:
ingressClassName: nginx
tls:
- hosts:
- sgs.localhost
secretName: sgs-tls-secret
rules:
- host: sgs.localhost
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sgs-app-service
port:
number: 80name:'swipeCompare',
url:'./Tools/ImageComparison/MeshComparisonPopup.html',
title:'Layer Comparison'
})
}
function home() {
if (SGWorld === null)
alert("TEF not loaded");
else
TEF.contentWindow.navigate.home();
}
functionOnRenderQualityChanged(quality) {
$("#buffer").text(quality)
return false;
}
function draw(type) {
var position = SGWorld.Window.PixelToWorld(SGWorld.Window.Rect.Width / 2, SGWorld.Window.Rect.Height / 2, 0).Position;
var radius = SGWorld.Navigate.GetPosition(0).DistanceTo(position) / 20;
var group = TEF.contentWindow.projectTree.getMyDataGroup();
switch (type) {
case 0:
SGWorld.Creator.CreateCircle(position, radius, SGWorld.Creator.CreateColor(0, 255, 0, 1), SGWorld.Creator.CreateColor(0, 0, 0, 0), group, "circle");
break;
case 1:
SGWorld.Creator.CreateArrow(position, radius, 4, SGWorld.Creator.CreateColor(0, 255, 0, 1), SGWorld.Creator.CreateColor(0, 0, 0, 0), group, "arrow");
break;
case 2:
SGWorld.Creator.CreateBox(position, radius, radius, radius, SGWorld.Creator.CreateColor(0, 255, 0, 1), SGWorld.Creator.CreateColor(120, 10, 200, 100), group, "box");
break;
default:
break;
}
TEF.contentWindow.projectTree.refreshMyData();
}
/**
* Handles received messages:
* If the mode is "loginToSSO", redirects the page to the SSO URL.
* If the mode is "loginSucceeded", sends the data back to the iframe and closes the modal dialog.
*/
functionreceiveMessage(data) {
var receivedObj = JSON.parse(data.data);
if (receivedObj.mode == "loginToSSO") {
location = receivedObj.ssoURL;
}
else if (receivedObj.mode == 'loginSucceeded') {
var iframe = document.getElementById('tef');
iframe.contentWindow.postMessage(data.data, '*');
$('#loginIframe').attr('src', '');
$('#loginContainerIframe').modal('hide');
}
}
if (window == undefined)
attachEvent('onmessage', receiveMessage, false);
else
window.addEventListener('message', receiveMessage, false);
/*
* Displays the login modal dialog.
*/
function login() {
varencodedRedirectURL = encodeURIComponent(location.href);
// Replace [SERVER_URL] with your server URL. For example: https://cloud.skylineglobe.com
var serverURL = "[SERVER_URL]";
if(serverURL.toLowerCase().indexOf('[SERVER_URL]'.toLowerCase()) !== -1) {
alert("To enable this functionality, it is essential to input a valid SGS URL into the provided sample code, substituting the '[SERVER_URL]' placeholder.")
return;
}
$("#loginIframe").attr('src', serverURL + "/TELogin?fusionRedirectURL=" + encodedRedirectURL);
$('#loginContainerIframe').modal('show');
// Attaches a click event listener to the close button in the login modal dialog.
$('#loginContainerIframe .close').click(function () {
$('#loginIframe').attr('src', '');
$('#loginContainerIframe').modal('hide');
});
}
</script>
</head>
<body>
<div class="container">
<div class="row">
<div class="jumbotron p-0">
<h1 class="i18n display-4">My Application</h1>
<p class="i18n lead">3D map of the city</p>
<hr class="my-4">
</div>
</div>
<div class="row">
<div class="col-3 leftBar">
<p class="caption"><strong>Start TEF</strong></p>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="loadTEF(true);">Load Full App</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="loadTEF(false)">Load Map</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="loadPartialTEF()">Load Custom App</button> <br>
<hr>
<div id="analysisButtons" style="display:none">
<p class="caption"><strong>Tools</strong></p>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="distance();">Distance</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="area();">Area</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="volume();">Volume</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="profile();">Profile</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="comparison()">Comparison</button>
<hr>
<p class="caption"><strong>Actions</strong></p>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="rotate()">Rotate</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="zoomIn()">Zoom In</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="home()">Home</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="draw(0);">Draw Circle</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="draw(1);">Draw Arrow</button>
<button class="i18n btn btn-secondary my-1 mx-md-1 mx-lg-2" onclick="draw(2);">Draw Box</button>
<hr>
<p class="caption"><strong>Events</strong></p>
<div class="lead pl-4">
Quality: <span id="buffer"></span>
</div>
</div>
</div>
<div class="col-8">
<iframe id='tef' src="" class="tef"></iframe>
</div>
<!-- The modal dialog that opens the login page -->
<div id="loginContainerIframe" class="modal fade">
<div class="modal-dialog resizable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Log In to SkylineGlobe Server</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" style="border: none; background-color: white; width: 20px; height: 20px;">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!-- The iframe that will be used for login -->
<iframe id="loginIframe" src="" frameborder="0" width="100%" height="700"></iframe>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>