// #region Initialization console.log( "\nBegin initialization ##################################################" ); import angular from "angular"; angular != undefined ? console.log("\tLoaded AngularJs.") : console.error("\tFailed to load AngularJs."); import L, { PopupEvent, Popup } from "leaflet"; L != undefined ? console.log("\tLoaded Leaflet.") : console.error("\tFailed to load Leaflet."); import UTMLatLng from "utm-latlng"; UTMLatLng != undefined ? console.log("\tLoaded utm-latlng.") : console.error("\tFailed to load utm-latlng."); const isMobile: boolean = /Android/i.test(navigator.userAgent); // Check for mobile browser console.log( `\tDetected ${ isMobile ? "mobile browser." : "desktop browser, disabling AR features." }` ); // import AR from "scripts/architect" declare var AR: any; // Is loaded in html from scripts/architect.js // AngularJS const ngApp = angular.module("main", []); console.log("\tInitialized AngularJs."); console.log( "Initialization finished ###############################################" ); // #endregion // #region Helpers module Calc { // Converts radians to degrees export function RadToDeg(rad: number): number { return rad * (180 / Math.PI); } // Converts degrees to radians export function DegToRad(deg: number): number { return (deg * Math.PI) / 180; } // Rotates a point around 0,0 by an angle in radians export function RotatePoint( point: { x: number; y: number }, angle: number ): { x: number; y: number } { return { x: point.x * Math.cos(angle) - point.y * Math.sin(angle), y: point.x * Math.sin(angle) + point.y * Math.cos(angle) }; } // UTM Coordinates class UTM { Easting: number; Northing: number; ZoneNumber: number; ZoneLetter: string; constructor(e: number, n: number, zn: number, zl: string) { this.Easting = e; this.Northing = n; this.ZoneNumber = zn; this.ZoneLetter = zl; } } const _precision: number = 7; // Precision for LatLon to UTM conversion const _utm = new UTMLatLng("WGS 84"); // Create UTM/LatLng converter for WGS84 projection // Wrapper for LatLon to UTM conversion export function LatLonToUtm(lat: number, lon: number) { return _utm.convertLatLngToUtm(lat, lon, _precision); } // Wrapper for UTM to LatLon conversion export function UtmToLatLon(utm: UTM): { lat: number; lon: number } { let temp = _utm.convertUtmToLatLng( utm.Easting, utm.Northing, utm.ZoneNumber, utm.ZoneLetter ); return { lat: temp.lat, lon: temp.lng }; } } // Leaflet marker with additional info to represent a mounting point class MountingPoint extends L.Marker { SupportedAssets: number[] = []; ID: number = -1; } // Represents all levels with the same designation, // Stores the mounting points of those levels class Layer { Name: string; Ids: number[]; Selected: boolean; LLayer: L.LayerGroup; constructor(name) { this.Name = name; this.Ids = []; this.Selected = true; this.LLayer = L.layerGroup(undefined); } } // Returns true if str is undefined, empty or null function IsStringEmpty(str: string): boolean { return str == undefined || str == "" || str == null; } // jQuery like shortcut to get html elements function $(query: string): HTMLElement | null { return document.querySelector(query); } // Retrieves the angular $scope from outside a controller function GetScope(): any { return angular.element($("body") as Element).scope() as any; } function Status(str: string): void { GetScope().status = str; console.log(str); ($("#progressBar") as HTMLProgressElement).value++; } // #endregion // Main AngularJs controller ngApp.controller("ctrl", $scope => { let data: any = {}; // Will hold the websocket response let t0: number | null = 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 = (ip: string): void => { $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 = () => { console.log("\tConnected."); ws.send( JSON.stringify({ type: "getEssentials", uuid: "UUID" }) ); }; // Handles the response of the websocket ws.onmessage = 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 = event => { if (event.code > 1000) { t0 = null; $scope.status = ""; alert(`Error when getting websocket response.\n${event.code}`); $scope.disableInput = false; } }; }; /** @function */ $scope.loadDataFromCache = () => { $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(); let _data = unescape(window.localStorage.getItem("data") as string); data = JSON.parse(_data).data; console.log( `\tLoaded ${Math.round( new TextEncoder().encode(_data).length / 1000 )}kb from cache.` ); $scope.$broadcast("dataLoaded"); } catch { $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", () => { let _data: any = 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 let map = new L.Map("map").setView( new L.LatLng(53.0528064472226, 8.89412999153137), 8 ); let imageBounds = new L.LatLngBounds([ [53.0528064472226, 8.89412999153137], [53.056520842139, 8.90030980110168] ]); let mapImage = L.imageOverlay("img/RDE2.jpg", imageBounds); map.addLayer(mapImage); map.fitBounds(imageBounds); // #endregion // #region BUILDINGS // Get buildings let buildings: any[] = _data.objects; Status(`\tParsing ${buildings.length} buildings...`); // Create GeoJson from buidling footprints let footprintPolygons: GeoJSON.FeatureCollection = { type: "FeatureCollection", features: [] }; for (let currentBuilding of buildings) { // Check if current builing has footprint data, if not: skip if (!(currentBuilding.path[0] == undefined)) { // Holds footprint coordinates of the current building let footprintCoords: number[][][] = [[]]; // Parse footprint coordinates from current building for (let pos of currentBuilding.path) { 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 let currentFootprint: GeoJSON.Feature = { 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 let footprintGeoJson = L.geoJSON(footprintPolygons, { onEachFeature: (feature, layer) => { layer.bindPopup(`
| ID | ${m.id} |
| Object ID | ${m.objectId} |
| Supported Asset Type IDs | ${m.supportedAssetTypeIds} |