"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); // #region Initialization console.log("\nBegin initialization ##################################################"); var angular_1 = __importDefault(require("angular")); angular_1.default != undefined ? console.log("\tLoaded AngularJs.") : console.error("\tFailed to load AngularJs."); var leaflet_1 = __importDefault(require("leaflet")); leaflet_1.default != undefined ? console.log("\tLoaded Leaflet.") : console.error("\tFailed to load Leaflet."); var utm_latlng_1 = __importDefault(require("utm-latlng")); utm_latlng_1.default != undefined ? console.log("\tLoaded utm-latlng.") : console.error("\tFailed to load utm-latlng."); var isMobile = /Android/i.test(navigator.userAgent); // Check for mobile browser console.log("\tDetected " + (isMobile ? "mobile browser." : "desktop browser, disabling AR features.")); // AngularJS var ngApp = angular_1.default.module("main", []); console.log("\tInitialized AngularJs."); console.log("Initialization finished ###############################################"); // #endregion // #region Helpers var Calc; (function (Calc) { // Converts radians to degrees function RadToDeg(rad) { return rad * (180 / Math.PI); } Calc.RadToDeg = RadToDeg; // Converts degrees to radians function DegToRad(deg) { return (deg * Math.PI) / 180; } Calc.DegToRad = DegToRad; // Rotates a point around 0,0 by an angle in radians function RotatePoint(point, angle) { return { x: point.x * Math.cos(angle) - point.y * Math.sin(angle), y: point.x * Math.sin(angle) + point.y * Math.cos(angle) }; } Calc.RotatePoint = RotatePoint; // UTM Coordinates var UTM = /** @class */ (function () { function UTM(e, n, zn, zl) { this.Easting = e; this.Northing = n; this.ZoneNumber = zn; this.ZoneLetter = zl; } return UTM; }()); var _precision = 7; // Precision for LatLon to UTM conversion var _utm = new utm_latlng_1.default("WGS 84"); // Create UTM/LatLng converter for WGS84 projection // Wrapper for LatLon to UTM conversion function LatLonToUtm(lat, lon) { return _utm.convertLatLngToUtm(lat, lon, _precision); } Calc.LatLonToUtm = LatLonToUtm; // Wrapper for UTM to LatLon conversion function UtmToLatLon(utm) { var temp = _utm.convertUtmToLatLng(utm.Easting, utm.Northing, utm.ZoneNumber, utm.ZoneLetter); return { lat: temp.lat, lon: temp.lng }; } Calc.UtmToLatLon = UtmToLatLon; })(Calc || (Calc = {})); // Leaflet marker with additional info to represent a mounting point var MountingPoint = /** @class */ (function (_super) { __extends(MountingPoint, _super); function MountingPoint() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.SupportedAssets = []; _this.ID = -1; return _this; } return MountingPoint; }(leaflet_1.default.Marker)); // Represents all levels with the same designation, // Stores the mounting points of those levels var Layer = /** @class */ (function () { function Layer(name) { this.Name = name; this.Ids = []; this.Selected = true; this.LLayer = leaflet_1.default.layerGroup(undefined); } return Layer; }()); // Returns true if str is undefined, empty or null function IsStringEmpty(str) { return str == undefined || str == "" || str == null; } // jQuery like shortcut to get html elements function $(query) { return document.querySelector(query); } // Retrieves the angular $scope from outside a controller function GetScope() { return angular_1.default.element($("body")).scope(); } function Status(str) { GetScope().status = str; console.log(str); $("#progressBar").value++; } // #endregion // Main AngularJs controller ngApp.controller("ctrl", function ($scope) { var data = {}; // Will hold the websocket response var t0 = null; // Measures the time it takes to load and parse $scope.disableInput = false; // True when loadData kicks in, disables the inputs $scope.connected = false; // True when websocket response was received $scope.status = undefined; // String that will be shown while connecting // Writes the websocket response to data when the load button is clicked /** @function */ $scope.loadDataFromWS = function (ip) { $scope.disableInput = true; console.log("Begin loading data ####################################################"); t0 = performance.now(); // Get start time // Default value for ip if nothing was entered ip = IsStringEmpty(ip) ? "ws://192.168.133.140:2080" : ip; // Catch invalid ips try { var ws = new WebSocket(ip); // Connect to the websocket } catch (e) { alert("Invalid IP"); return; } Status("\tConnecting to " + ip + "..."); // Connect to the web socket ws.onopen = function () { console.log("\tConnected."); ws.send(JSON.stringify({ type: "getEssentials", uuid: "UUID" })); }; // Handles the response of the websocket ws.onmessage = function (response) { console.log("\tFetched " + Math.round(new TextEncoder().encode(response.data).length / 1000) + "kb."); data = JSON.parse(response.data).data; // Parse websocket response to data window.localStorage.setItem("data", escape(response.data)); $scope.$broadcast("dataLoaded"); // Call response event to kick in the map and selection GUI }; // Handle bad websocket responses ws.onclose = function (event) { if (event.code > 1000) { t0 = null; $scope.status = ""; alert("Error when getting websocket response.\n" + event.code); $scope.disableInput = false; } }; }; /** @function */ $scope.loadDataFromCache = function () { $scope.disableInput = true; console.log("Begin loading data ####################################################"); t0 = performance.now(); Status("Loading data from cache..."); try { if (window.localStorage.getItem("data") == null) throw new Error(); var _data = unescape(window.localStorage.getItem("data")); data = JSON.parse(_data).data; console.log("\tLoaded " + Math.round(new TextEncoder().encode(_data).length / 1000) + "kb from cache."); $scope.$broadcast("dataLoaded"); } catch (_a) { $scope.status = ""; $scope.disableInput = false; t0 = null; console.log("\tCould not load data from cache."); alert("No data cached."); } }; // Handles websocket response event $scope.$on("dataLoaded", function () { var _data = data; $scope.connected = true; // Switches the connection menu for the mounting point selection $scope.loading = true; // Disables the GUI while doing stuff // #region MAP console.log("\tCreating map."); // Create the map, set view and image var map = new leaflet_1.default.Map("map").setView(new leaflet_1.default.LatLng(53.0528064472226, 8.89412999153137), 8); var imageBounds = new leaflet_1.default.LatLngBounds([ [53.0528064472226, 8.89412999153137], [53.056520842139, 8.90030980110168] ]); var mapImage = leaflet_1.default.imageOverlay("img/RDE2.jpg", imageBounds); map.addLayer(mapImage); map.fitBounds(imageBounds); // #endregion // #region BUILDINGS // Get buildings var buildings = _data.objects; Status("\tParsing " + buildings.length + " buildings..."); // Create GeoJson from buidling footprints var footprintPolygons = { type: "FeatureCollection", features: [] }; for (var _i = 0, buildings_1 = buildings; _i < buildings_1.length; _i++) { var currentBuilding = buildings_1[_i]; // Check if current builing has footprint data, if not: skip if (!(currentBuilding.path[0] == undefined)) { // Holds footprint coordinates of the current building var footprintCoords = [[]]; // Parse footprint coordinates from current building for (var _a = 0, _b = currentBuilding.path; _a < _b.length; _a++) { var pos = _b[_a]; footprintCoords[0].push([ // Convert from radians to degrees and add to the array Calc.RadToDeg(pos.lon), Calc.RadToDeg(pos.lat) ]); } // Last point has to be the same as the first point so we add it again footprintCoords[0].push([ Calc.RadToDeg(currentBuilding.path[0].lon), Calc.RadToDeg(currentBuilding.path[0].lat) ]); // Create new GeoJson Feature with building footprint var currentFootprint = { type: "Feature", properties: { name: currentBuilding.designation }, geometry: { type: "Polygon", coordinates: footprintCoords } }; // Add GeoJson feature to feature collection footprintPolygons.features.push(currentFootprint); } } // Draw building footprints to map, add name popups var footprintGeoJson = leaflet_1.default.geoJSON(footprintPolygons, { onEachFeature: function (feature, layer) { layer.bindPopup("

" + feature.properties.name + "

"); } }).addTo(map); // #endregion // #region LEVELS var levels = _data.levels; // Levels are the actual floors in the building var layers = []; // Layers are their representation on the map Status("\tParsing " + levels.length + " levels..."); for (var _c = 0, levels_1 = levels; _c < levels_1.length; _c++) { var level = levels_1[_c]; // Create a layer for each level var add = true; for (var _d = 0, layers_1 = layers; _d < layers_1.length; _d++) { var layer = layers_1[_d]; // Levels with duplicate names will be on the same layer if (level.designation === layer.Name) { add = false; layer.Ids.push(level.id); break; } } if (add) { // Create new Layer if level is not a duplicate var newLayer = new Layer(level.designation); newLayer.Ids.push(level.id); layers.push(newLayer); } } var outdoorLayer = new Layer("outdoor"); // Create a layer for mounting points outside of buildings outdoorLayer.Ids = [-1]; var footprintLayer = new Layer("buildings"); // Create a layer for the building footprint footprintLayer.LLayer = footprintGeoJson; layers.push(outdoorLayer); // Add outdoor and footprint layers layers.push(footprintLayer); $scope.layers = layers; // Add layers to the checkbox menu // #endregion // #region MOUNTING POINTS // Get mounting points var mountingPoints = _data.mountingPoints; Status("\tParsing " + mountingPoints.length + " mounting points..."); for (var _e = 0, mountingPoints_1 = mountingPoints; _e < mountingPoints_1.length; _e++) { var m = mountingPoints_1[_e]; for (var _f = 0, buildings_2 = buildings; _f < buildings_2.length; _f++) { var b = buildings_2[_f]; if (b.id == m.objectId) { // Current mounting point is in current buidling? b.utmRefPoint = Calc.LatLonToUtm(Calc.RadToDeg(b.refPoint.lat), Calc.RadToDeg(b.refPoint.lon)); // Converts refPoint of the building to UTM m.fixedPosition = Calc.RotatePoint({ x: m.position3d.x, y: m.position3d.y }, Calc.DegToRad(b.rotationDegree)); // Rotates coords of the mounting point by rotationDegree of the building m.absoluteUtm = b.utmRefPoint; m.absoluteUtm.Easting += m.fixedPosition.x; m.absoluteUtm.Northing += m.fixedPosition.y; m.absoluteLatLon = Calc.UtmToLatLon(m.absoluteUtm); // Add refPoint to the relative position and convert back to lat/lng var arButtonHtml = isMobile ? "\n\t\t\t\t\t\t\t\tShow in AR\n\t\t\t\t\t\t\t" : "AR not supported on desktop."; for (var _g = 0, layers_2 = layers; _g < layers_2.length; _g++) { var l = layers_2[_g]; for (var _h = 0, _j = l.Ids; _h < _j.length; _h++) { var i = _j[_h]; if (i == m.levelId) { // Current mouting point is on current level? var marker = new MountingPoint( // Create a marker for the mounting point new leaflet_1.default.LatLng(m.absoluteLatLon.lat, m.absoluteLatLon.lon), { icon: leaflet_1.default.icon({ iconUrl: "img/marker.png", iconSize: [30, 30] }) }) .bindPopup( // Create a popup with info about the mounting point "

\n\t\t\t\t\t\t\t\t\t\t" + (IsStringEmpty(m.tag) ? m.designation : m.tag) + "\n\t\t\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
ID" + m.id + "
Object ID" + m.objectId + "
Supported Asset Type IDs" + m.supportedAssetTypeIds + "
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t" + arButtonHtml + "\n\t\t\t\t\t\t\t\t\t\t") .addTo(map); // Add it to the map marker.SupportedAssets = // Add info about supported asset types m.supportedAssetTypeIds; marker.ID = m.id; // Add mounting point id l.LLayer.addLayer(marker); // Add marker to corresponding layer } } } } } } $scope.mountingPoints = mountingPoints; // #endregion // #region ASSETS // Get asset types var assets = _data.assetTypes; Status("\tParsing " + assets.length + " assets..."); // Add asset types to the dropdown menu $scope.assets = { availableAssets: assets, selectedAsset: assets[0] }; // #endregion // #region ANGULAR FUNCTIONS // Called by either the layer or asset selection /** @function */ $scope.SelectionChanged = function () { var points = 0; for (var _i = 0, layers_3 = layers; _i < layers_3.length; _i++) { var layer = layers_3[_i]; if (layer.Selected) { map.addLayer(layer.LLayer); // Add selected layers // Hacky type conversion to get the supported asset types from each mounting point on a layer // If it's stupid but it works it's not stupid for (var _a = 0, _b = layer.LLayer.getLayers(); _a < _b.length; _a++) { var m = _b[_a]; try { for (var _c = 0, _d = m.SupportedAssets; _c < _d.length; _c++) { var a = _d[_c]; if (a == $scope.assets.selectedAsset.id) { m.addTo(map); // Add markers for selected asset type ++points; } else { m.removeFrom(map); // Remove all other markers } } } catch (_e) { /** Sometimes theres an error here... * Doesn't seem to interfere with anything though and * I don't want to see it in the console */ } } } else map.removeLayer(layer.LLayer); // Remove unselected layers } console.log("Selected " + points + " mounting point" + (points == 1 ? "" : "s") + "."); }; // Filter for selected mounting point table and count /** @function */ $scope.MountingPointFilter = function () { return function (mp) { var correctAssetType = false; for (var _i = 0, _a = mp.supportedAssetTypeIds; _i < _a.length; _i++) { var i = _a[_i]; if (i == $scope.assets.selectedAsset.id) { correctAssetType = true; break; } } // Current mounting point supports selected asset type var correctLayer = false; for (var _b = 0, layers_4 = layers; _b < layers_4.length; _b++) { var layer = layers_4[_b]; for (var _c = 0, _d = layer.Ids; _c < _d.length; _c++) { var i = _d[_c]; if (layer.Selected && i == mp.levelId) { correctLayer = true; break; } } } // Current mounting point is on a selected layer var foundBySearch = IsStringEmpty($scope.mpSearch) ? true // Automatically true if search input empty : (IsStringEmpty(mp.tag) ? mp.designation : mp.tag).indexOf($scope.mpSearch) >= 0 || mp.id.toString().indexOf($scope.mpSearch) >= 0; // true if tag, designation or id contain search input return correctAssetType && correctLayer && foundBySearch; }; }; // Function for the Show button /** @function */ $scope.ShowMp = function (mp) { for (var _i = 0, layers_5 = layers; _i < layers_5.length; _i++) { var l = layers_5[_i]; // Not this again... for (var _a = 0, _b = l.LLayer.getLayers(); _a < _b.length; _a++) { var m = _b[_a]; if (m.ID == mp.id) { // Find clicked mounting point m.openPopup(); // Open corresponding popup map.panTo(m.getLatLng()); // Center map on the popup break; } } } }; // #endregion // Get final time and format it var time = t0 != null ? "Finished loading data in " + (performance.now() - t0) + "ms ########################################" : "Finished loading data #################################################"; console.log(time); $scope.SelectionChanged(); // Make initial map state fit the initial menu state $scope.loading = false; // Done loading $scope.$apply(); }); }); // #region WIKITUDE FUNCTIONS var currentPOI = undefined; // Holds current wikitude ar marker // Shows an augmented reality marker vor the selected mounting point function _displayInAr(id) { var $scope = GetScope(); // Retrieve angular scope var mountingPoint; for (var _i = 0, _a = $scope.mountingPoints; _i < _a.length; _i++) { var mp = _a[_i]; if (mp.id == id) { mountingPoint = mp; break; } } // Get correct mounting point var name = IsStringEmpty(mountingPoint.tag) ? mountingPoint.designation : mountingPoint.tag; $scope.arMountingPoint = name; // Display mounting point tag/designation currentPOI = new AR.GeoObject([ new AR.GeoLocation(mountingPoint.absoluteLatLon.lat, mountingPoint.absoluteLatLon.lon) ], { drawables: { cam: [ new AR.ImageDrawable(new AR.ImageResource("img/armarker.png"), 2.5, { zOrder: 0, opacity: 1 }), new AR.Label(name, 1, { zOrder: 1, style: { textColor: "#ffffff" } }) ], indicator: new AR.ImageDrawable(new AR.ImageResource("img/arindicator.png"), 0.1, { enabled: true, verticalAnchor: AR.CONST.VERTICAL_ANCHOR.TOP }) } }); // Create AR marker $scope.arMode = true; // Hide map and menu $scope.$apply(); console.log("Switched to AR mode."); } // Goes back to the map view function _backFromAr() { var $scope = GetScope(); // Retrieve angular scope $scope.arMountingPoint = ""; // Clear name display $scope.arMode = false; // Show map and menu again currentPOI.destroy(); currentPOI = undefined; $scope.$apply(); console.log("Switched to map mode."); } // Attach functions window.Functions = window.Functions || { DisplayInAr: _displayInAr, BackFromAr: _backFromAr }; // #endregion // #endregion