Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+13
@@ -0,0 +1,13 @@
|
||||
import { DEBUG } from "./main";
|
||||
|
||||
export default class Asset {
|
||||
public Name: string;
|
||||
public Id: number;
|
||||
|
||||
constructor(Raw: { id: number; designation: string; deviceClass: number }) {
|
||||
this.Name = Raw.designation;
|
||||
this.Id = Raw.id;
|
||||
|
||||
if (DEBUG) console.log(`new Asset() with Id = ${this.Id}`);
|
||||
}
|
||||
}
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
import { Helpers } from "./Helpers";
|
||||
import { DEBUG } from "./main";
|
||||
|
||||
export default class Building {
|
||||
public Footprint: GeoJSON.Feature | null;
|
||||
public Name: string;
|
||||
public Id: number;
|
||||
public RefPoint: Helpers.Coordinates.Utm;
|
||||
public RotationDegree: number;
|
||||
|
||||
constructor(Raw: {
|
||||
id: number;
|
||||
designation: string;
|
||||
areaId: number;
|
||||
refPoint: {
|
||||
lat: number;
|
||||
lon: number;
|
||||
};
|
||||
street: string;
|
||||
number: string;
|
||||
path: {
|
||||
lat: number;
|
||||
lon: number;
|
||||
}[];
|
||||
pathRelative: {
|
||||
lat: number;
|
||||
lon: number;
|
||||
}[];
|
||||
rotationDegree: number;
|
||||
}) {
|
||||
try {
|
||||
let footprintCoords = [
|
||||
Raw.path.map(p => [
|
||||
Helpers.Calculations.RadToDeg(p.lon),
|
||||
Helpers.Calculations.RadToDeg(p.lat)
|
||||
])
|
||||
];
|
||||
|
||||
footprintCoords[0].push([
|
||||
Helpers.Calculations.RadToDeg(Raw.path[0].lon),
|
||||
Helpers.Calculations.RadToDeg(Raw.path[0].lat)
|
||||
]); // Last point == first point
|
||||
|
||||
this.Footprint = {
|
||||
type: "Feature",
|
||||
properties: {
|
||||
name: Raw.designation
|
||||
},
|
||||
geometry: {
|
||||
type: "Polygon",
|
||||
coordinates: footprintCoords
|
||||
}
|
||||
};
|
||||
} catch {
|
||||
this.Footprint = null;
|
||||
}
|
||||
|
||||
this.RefPoint = new Helpers.Coordinates.LatLon(
|
||||
Helpers.Calculations.RadToDeg(Raw.refPoint.lat),
|
||||
Helpers.Calculations.RadToDeg(Raw.refPoint.lon)
|
||||
).ToUtm();
|
||||
|
||||
this.Name = Raw.designation;
|
||||
this.Id = Raw.id;
|
||||
this.RotationDegree = Raw.rotationDegree;
|
||||
|
||||
if (DEBUG) console.log(`new Building() with Id = ${this.Id}`);
|
||||
}
|
||||
}
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
import { Helpers, _, MOBILE } from "./Helpers";
|
||||
|
||||
export module Functions {
|
||||
|
||||
//#region private
|
||||
async function checkForPort(ip: string): Promise<number> {
|
||||
return new Promise<number>(async (resolve, reject) => {
|
||||
new Helpers.WebSocketRequest(
|
||||
ip,
|
||||
JSON.stringify({
|
||||
type: "servicePortsInformationRequest",
|
||||
uuid: Helpers.GenUuid()
|
||||
})
|
||||
)
|
||||
.GetResponseAsync()
|
||||
.then(response => {
|
||||
for (let p of JSON.parse(response).data[0].services) {
|
||||
if (p.nameAbbrevation === "ism") {
|
||||
return resolve(p.ports[0].number);
|
||||
}
|
||||
}
|
||||
return reject();
|
||||
})
|
||||
.catch(() => {
|
||||
return reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function retrieveDataIp(tryLocalhostFirst: boolean): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const localhost: string = "ws://localhost";
|
||||
const defaultIp: string = "ws://192.168.133.140";
|
||||
const portToCheck: number = 7300;
|
||||
if (tryLocalhostFirst) {
|
||||
checkForPort(`${localhost}:${portToCheck}`)
|
||||
.then(port => {
|
||||
return resolve(`${localhost}:${port}`);
|
||||
})
|
||||
.catch(() =>
|
||||
checkForPort(`${defaultIp}:${portToCheck}`)
|
||||
.then(port => {
|
||||
return resolve(`${defaultIp}:${port}`);
|
||||
})
|
||||
.catch(() => {
|
||||
return reject();
|
||||
})
|
||||
);
|
||||
} else {
|
||||
checkForPort(`${defaultIp}:${portToCheck}`)
|
||||
.then(port => resolve(`${defaultIp}:${port}`))
|
||||
.catch(() =>
|
||||
checkForPort(`${localhost}:${portToCheck}`)
|
||||
.then(port => {
|
||||
return resolve(`${localhost}:${port}`);
|
||||
})
|
||||
.catch(() => {
|
||||
return reject();
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadData(ip: string): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
new Helpers.WebSocketRequest(
|
||||
ip,
|
||||
JSON.stringify({
|
||||
type: "getEssentials",
|
||||
uuid: Helpers.GenUuid()
|
||||
})
|
||||
)
|
||||
.GetResponseAsync()
|
||||
.then(data => resolve(JSON.parse(data).data))
|
||||
.catch(() => {
|
||||
return reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function manualLoadData(): Promise<any> {
|
||||
return new Promise<any>(async (resolve, reject) => {
|
||||
let data: any;
|
||||
let serverIp: string = "ws://192.168.133.140:2080";
|
||||
do {
|
||||
serverIp = prompt(
|
||||
"Unable to load data automatically. Please enter ip:",
|
||||
serverIp
|
||||
) as string;
|
||||
try {
|
||||
data = await loadData(serverIp);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
} while (_(data));
|
||||
webSocketIp = serverIp;
|
||||
return resolve(data);
|
||||
});
|
||||
}
|
||||
|
||||
function storeInCache(key: string, value: any): void {
|
||||
window.localStorage.setItem(key, escape(JSON.stringify(value)));
|
||||
}
|
||||
|
||||
function retrieveFromCache(key: string): any | never {
|
||||
let value = window.localStorage.getItem(key);
|
||||
if (!_(value)) return JSON.parse(unescape(value as string));
|
||||
else throw new Error();
|
||||
}
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* Tries to load the data automatically from the websocket,
|
||||
* then from cache, then from the user. Always caches the
|
||||
* response.
|
||||
*/
|
||||
export async function GetData(): Promise<any> {
|
||||
return new Promise<any>(async (resolve, reject) => {
|
||||
var data: any;
|
||||
try {
|
||||
let serverIp = await retrieveDataIp(!MOBILE);
|
||||
data = await loadData(serverIp);
|
||||
webSocketIp = serverIp;
|
||||
} catch {
|
||||
try {
|
||||
data = retrieveFromCache("data");
|
||||
} catch {
|
||||
data = await manualLoadData();
|
||||
}
|
||||
} finally {
|
||||
storeInCache("data", data);
|
||||
return resolve(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var webSocketIp: string | null = null;
|
||||
|
||||
export async function GetMountedAssetIp(Id: number): Promise<string> {
|
||||
return new Promise<string>(async (resolve, reject) => {
|
||||
if (_(webSocketIp)) return reject();
|
||||
try {
|
||||
let ip: string = JSON.parse(
|
||||
await new Helpers.WebSocketRequest(
|
||||
webSocketIp as string,
|
||||
JSON.stringify({
|
||||
type: "getAssetState",
|
||||
uuid: Helpers.GenUuid(),
|
||||
data: {
|
||||
assetId: Id
|
||||
}
|
||||
})
|
||||
).GetResponseAsync()
|
||||
).data.ipAddr;
|
||||
if (!_(ip)) return resolve(`http://${ip}/`);
|
||||
else return reject();
|
||||
} catch {
|
||||
return reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
import * as UTM from "utm";
|
||||
import angular from "angular";
|
||||
|
||||
/**
|
||||
* Checks for emptyness
|
||||
* @returns obj == undefined || obj == null || obj == ""
|
||||
*/
|
||||
export function _(obj: any): boolean {
|
||||
return obj == undefined || obj == null || obj == "" || obj == [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a DOM element
|
||||
* @returns document.querySelector(query)
|
||||
*/
|
||||
export function $(query: string): HTMLElement | null {
|
||||
return document.querySelector(query);
|
||||
}
|
||||
|
||||
export const MOBILE: boolean = /Android/i.test(navigator.userAgent);
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
includes(obj: any): boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the array includes given element
|
||||
*/
|
||||
Array.prototype.includes = function(obj: any) {
|
||||
return this.find(element => element == obj) != undefined;
|
||||
};
|
||||
|
||||
export module Helpers {
|
||||
export namespace Calculations {
|
||||
export function RadToDeg(rad: number): number {
|
||||
return rad * (180 / Math.PI);
|
||||
}
|
||||
|
||||
export function DegToRad(deg: number): number {
|
||||
return (deg * Math.PI) / 180;
|
||||
}
|
||||
|
||||
export function RotatePoint(
|
||||
point: { x: number; y: number },
|
||||
angleRad: number
|
||||
): { x: number; y: number } {
|
||||
return {
|
||||
x: point.x * Math.cos(angleRad) - point.y * Math.sin(angleRad),
|
||||
y: point.x * Math.sin(angleRad) + point.y * Math.cos(angleRad)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves angular scope
|
||||
*/
|
||||
export function GetScope(): any {
|
||||
return angular.element($("body") as Element).scope() as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArAssets-YYYY-MM-DDTHH:mm:ss.sssZ
|
||||
*/
|
||||
export function GenUuid(): string {
|
||||
return `ArAssets-${new Date().toISOString()}`;
|
||||
}
|
||||
|
||||
export class WebSocketRequest {
|
||||
/**
|
||||
* Execute with .GetResponseAsync()
|
||||
*/
|
||||
constructor(private Ip: string, private Request: string) {}
|
||||
|
||||
/**
|
||||
* Resolves into the response of the websocket
|
||||
*/
|
||||
public async GetResponseAsync(): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
try {
|
||||
let ws = new WebSocket(this.Ip);
|
||||
ws.onopen = () => {
|
||||
ws.send(this.Request);
|
||||
};
|
||||
ws.onmessage = response => {
|
||||
ws.close();
|
||||
return resolve(response.data);
|
||||
};
|
||||
ws.onclose = event => {
|
||||
if (event.code > 1000) return reject(event.reason);
|
||||
};
|
||||
ws.onerror = error => {
|
||||
return reject(error);
|
||||
};
|
||||
} catch {
|
||||
return reject(); // Just to be sure
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export async function GetPositionAsync(): Promise<Coordinates.LatLon> {
|
||||
return new Promise<Coordinates.LatLon>((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(pos => {
|
||||
resolve(
|
||||
new Coordinates.LatLon(
|
||||
pos.coords.latitude,
|
||||
pos.coords.longitude
|
||||
)
|
||||
);
|
||||
});
|
||||
reject();
|
||||
});
|
||||
}
|
||||
|
||||
export namespace Coordinates {
|
||||
// const utm: UTMLatLng = new UTMLatLng("WGS 84");
|
||||
// const PRECISION: number = 7;
|
||||
|
||||
export class LatLon {
|
||||
constructor(public Lat: number, public Lon: number) {}
|
||||
|
||||
public ToUtm(): Utm {
|
||||
let temp = UTM.fromLatLon(
|
||||
this.Lat,
|
||||
this.Lon
|
||||
);
|
||||
return new Utm(
|
||||
temp.easting,
|
||||
temp.northing,
|
||||
temp.zoneNum,
|
||||
temp.zoneLetter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Utm {
|
||||
constructor(
|
||||
public Easting: number,
|
||||
public Northing: number,
|
||||
public ZoneNumber: number,
|
||||
public ZoneLetter: string
|
||||
) {}
|
||||
|
||||
public ToLatLon(): LatLon {
|
||||
let temp = UTM.toLatLon(
|
||||
this.Easting,
|
||||
this.Northing,
|
||||
this.ZoneNumber,
|
||||
this.ZoneLetter
|
||||
);
|
||||
return new LatLon(temp.latitude, temp.longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
import L from "leaflet";
|
||||
import { DEBUG } from "./main";
|
||||
|
||||
export default class Layer {
|
||||
public LeafletLayer: L.LayerGroup = new L.LayerGroup();
|
||||
public Visible: boolean = true;
|
||||
public LayerIds: number[] = [];
|
||||
|
||||
constructor(public Name: string) {
|
||||
if (DEBUG) console.log(`new Layer() with Name = ${this.Name}`);
|
||||
}
|
||||
}
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
import L from "leaflet";
|
||||
import { DEBUG } from "./main";
|
||||
|
||||
export default class Location extends L.ImageOverlay {
|
||||
public Name: string;
|
||||
|
||||
constructor(Raw: {
|
||||
img: string;
|
||||
name: string;
|
||||
bounds: {
|
||||
min: {
|
||||
lat: number;
|
||||
lon: number;
|
||||
};
|
||||
max: {
|
||||
lat: number;
|
||||
lon: number;
|
||||
};
|
||||
};
|
||||
}) {
|
||||
super(
|
||||
`map/${Raw.img}`,
|
||||
new L.LatLngBounds([
|
||||
[Raw.bounds.min.lat, Raw.bounds.min.lon],
|
||||
[Raw.bounds.max.lat, Raw.bounds.max.lon]
|
||||
])
|
||||
);
|
||||
|
||||
this.Name = Raw.name;
|
||||
|
||||
if (DEBUG) console.log(`new Location() with Name = ${this.Name}`);
|
||||
}
|
||||
}
|
||||
Executable
+174
@@ -0,0 +1,174 @@
|
||||
import L from "leaflet";
|
||||
import Building from "./Building";
|
||||
import { Helpers, _, MOBILE } from "./Helpers";
|
||||
import { Functions } from "./Functions";
|
||||
import { DEBUG } from "./main";
|
||||
|
||||
export default class MountingPoint extends L.Marker {
|
||||
public Name: string = "";
|
||||
public Id: number = -1;
|
||||
public SupportedAssets: number[] = [];
|
||||
public Level: number = -1;
|
||||
public MountedAsset: number = -1;
|
||||
|
||||
private constructor() {
|
||||
super(new L.LatLng(0, 0));
|
||||
throw new Error("Use static method MountingPoint.Create() instead.");
|
||||
// Don't use this.
|
||||
// This is just here so you can't use the constructor from
|
||||
// outside the class
|
||||
}
|
||||
|
||||
/**
|
||||
* MountingPoint factory method
|
||||
*/
|
||||
public static async Create(
|
||||
Raw: {
|
||||
id: number;
|
||||
designation: string;
|
||||
tag: string;
|
||||
objectId: number;
|
||||
levelId: number;
|
||||
roomId: number;
|
||||
assetId: number;
|
||||
position3d: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
supportedAssetTypeIds: number[];
|
||||
},
|
||||
Building: Building
|
||||
): Promise<MountingPoint> {
|
||||
return new Promise<MountingPoint>(async resolve => {
|
||||
// Wrong building?
|
||||
if (Building.Id != Raw.objectId) throw new Error("Wrong building");
|
||||
|
||||
let rotatedRelativePosition = Helpers.Calculations.RotatePoint(
|
||||
{
|
||||
x: Raw.position3d.x,
|
||||
y: Raw.position3d.y
|
||||
},
|
||||
Helpers.Calculations.DegToRad(Building.RotationDegree)
|
||||
); // Rotate mp position around building rotation degree
|
||||
|
||||
let temp = Object.create(Building.RefPoint);
|
||||
temp.Easting += rotatedRelativePosition.x;
|
||||
temp.Northing += rotatedRelativePosition.y;
|
||||
let position = temp.ToLatLon();
|
||||
// Add building ref point to mp relative position
|
||||
|
||||
let mounted = Raw.assetId != -1;
|
||||
|
||||
let mp = new L.Marker(new L.LatLng(position.Lat, position.Lon), {
|
||||
icon: new L.Icon({
|
||||
iconUrl: mounted
|
||||
? "img/marker_mounted.png"
|
||||
: "img/marker_default.png",
|
||||
iconSize: [30, 30]
|
||||
})
|
||||
}) as MountingPoint; // Setup Leaflet marker
|
||||
|
||||
mp.Name = _(Raw.tag) ? Raw.designation : Raw.tag;
|
||||
mp.SupportedAssets = Raw.supportedAssetTypeIds || [];
|
||||
mp.Id = Raw.id;
|
||||
mp.Level = Raw.levelId;
|
||||
mp.MountedAsset = Raw.assetId;
|
||||
|
||||
let popup = document.createElement("div") as HTMLDivElement;
|
||||
|
||||
// <h1>
|
||||
let header = document.createElement("h2") as HTMLHeadingElement;
|
||||
header.innerText = mp.Name;
|
||||
|
||||
// <table>
|
||||
let table = document.createElement("table") as HTMLTableElement;
|
||||
|
||||
table.insertRow().insertCell().innerText = "ID";
|
||||
table.rows[0].insertCell().innerText = mp.Id.toString();
|
||||
|
||||
table.insertRow().insertCell().innerText = "Object ID";
|
||||
table.rows[1].insertCell().innerText = Raw.objectId.toString();
|
||||
|
||||
table.insertRow().insertCell().innerText = "Supported asset IDs";
|
||||
table.rows[2].insertCell().innerText = mp.SupportedAssets.toString();
|
||||
|
||||
// <tr>
|
||||
let asset = document.createElement("tr") as HTMLTableRowElement;
|
||||
try {
|
||||
let link = document.createElement("a") as HTMLAnchorElement;
|
||||
link.innerText = "Mounted asset ID";
|
||||
link.setAttribute(
|
||||
"href",
|
||||
await Functions.GetMountedAssetIp(mp.MountedAsset)
|
||||
);
|
||||
link.setAttribute("target", MOBILE ? "_parent" : "_blank");
|
||||
asset.insertCell().appendChild(link);
|
||||
} catch {
|
||||
asset.insertCell().innerText = "Mounted asset ID";
|
||||
}
|
||||
asset.insertCell().innerText = mp.MountedAsset.toString();
|
||||
if (mounted) table.appendChild(asset);
|
||||
|
||||
// <button>
|
||||
let button = document.createElement("button") as HTMLButtonElement;
|
||||
button.innerText = MOBILE ? "Show in AR" : "Raw info";
|
||||
button.onclick = MOBILE
|
||||
? () => window.AR.StartAr(mp.Id)
|
||||
: () =>
|
||||
window.MP.MoreInfo(
|
||||
escape(JSON.stringify(Raw, null, "\t"))
|
||||
);
|
||||
|
||||
// Build popup
|
||||
popup.appendChild(header);
|
||||
popup.appendChild(table);
|
||||
popup.appendChild(document.createElement("hr") as HTMLHRElement);
|
||||
popup.appendChild(button);
|
||||
|
||||
mp.bindPopup(popup);
|
||||
|
||||
if (DEBUG) console.log(`new MountingPoint() with ID = ${mp.Id}`);
|
||||
|
||||
return resolve(mp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _moreInfo(info: string) {
|
||||
let popup = open("", Helpers.GenUuid()) as Window;
|
||||
|
||||
// <pre>
|
||||
let display = popup.document.createElement("pre") as HTMLPreElement;
|
||||
display.innerHTML = unescape(info);
|
||||
display.style.fontSize = "15";
|
||||
|
||||
// <button>
|
||||
let finish = popup.document.createElement("button") as HTMLButtonElement;
|
||||
finish.innerHTML = "Done";
|
||||
finish.setAttribute("onclick", "window.close()");
|
||||
|
||||
// build popup
|
||||
popup.document.body.appendChild(popup.document.createElement(
|
||||
"hr"
|
||||
) as HTMLHRElement);
|
||||
popup.document.body.appendChild(display);
|
||||
popup.document.body.appendChild(popup.document.createElement(
|
||||
"hr"
|
||||
) as HTMLHRElement);
|
||||
popup.document.body.appendChild(finish);
|
||||
}
|
||||
|
||||
//#region
|
||||
declare global {
|
||||
interface Window {
|
||||
MP: {
|
||||
MoreInfo: (info: string) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
window.MP = window.MP || {
|
||||
MoreInfo: _moreInfo
|
||||
};
|
||||
//#endregion
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* This is only used in the mobile app
|
||||
*/
|
||||
|
||||
import { Helpers, $ } from "./Helpers";
|
||||
import MountingPoint from "./MountingPoint";
|
||||
import { DEBUG } from "./main";
|
||||
declare var AR: any; // Wikitude is loaded from html
|
||||
|
||||
var currentPoi: any = undefined;
|
||||
|
||||
function _startAr(mpId: number): void {
|
||||
let $scope = Helpers.GetScope();
|
||||
let mountingPoint: MountingPoint | undefined = undefined;
|
||||
|
||||
for (let mp of $scope.objects.mountingPoints as MountingPoint[]) {
|
||||
if (mp.Id == mpId) {
|
||||
mountingPoint = mp;
|
||||
break;
|
||||
}
|
||||
} // Get correct mounting point
|
||||
|
||||
mountingPoint = mountingPoint as MountingPoint; // Bruh.
|
||||
|
||||
currentPoi = new AR.GeoObject(
|
||||
[
|
||||
new AR.GeoLocation(
|
||||
mountingPoint.getLatLng().lat,
|
||||
mountingPoint.getLatLng().lng
|
||||
)
|
||||
],
|
||||
{
|
||||
drawables: {
|
||||
cam: [
|
||||
new AR.ImageDrawable(
|
||||
new AR.ImageResource("img/armarker.png"),
|
||||
2.5,
|
||||
{ zOrder: 0, opacity: 1 }
|
||||
),
|
||||
new AR.Label(mountingPoint.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.ar.current = mountingPoint.Name;
|
||||
$scope.ar.enabled = true;
|
||||
$scope.$apply();
|
||||
($("body") as HTMLBodyElement).style.backgroundColor = "transparent";
|
||||
}
|
||||
|
||||
function _endAr(): void {
|
||||
let $scope = Helpers.GetScope();
|
||||
|
||||
$scope.ar.current = "";
|
||||
$scope.ar.enabled = false;
|
||||
|
||||
currentPoi.destroy();
|
||||
currentPoi = undefined;
|
||||
|
||||
$scope.$apply();
|
||||
($("body") as HTMLBodyElement).style.backgroundColor = "white";
|
||||
}
|
||||
|
||||
//#region
|
||||
declare global {
|
||||
interface Window {
|
||||
AR: {
|
||||
StartAr: (mpId: number) => void;
|
||||
EndAr: () => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
window.AR = window.AR || {
|
||||
StartAr: _startAr,
|
||||
EndAr: _endAr
|
||||
};
|
||||
//#endregion
|
||||
|
||||
if (DEBUG) AR.logger.activateDebugMode();
|
||||
Executable
+240
@@ -0,0 +1,240 @@
|
||||
import angular from "angular";
|
||||
import L from "leaflet";
|
||||
import GeoJSON from "geojson";
|
||||
import { _ } from "./Helpers";
|
||||
import { Functions } from "./Functions";
|
||||
import Building from "./Building";
|
||||
import Layer from "./Layer";
|
||||
import Asset from "./Asset";
|
||||
import MountingPoint from "./MountingPoint";
|
||||
import Location from "./Location";
|
||||
|
||||
export const DEBUG: boolean = false; // Debug mode
|
||||
|
||||
const NGAPP: angular.IModule = angular.module("main", []);
|
||||
|
||||
NGAPP.controller("ctrl", async ($scope, $http) => {
|
||||
const dataRequest: any = Functions.GetData(); // Request data from websocket
|
||||
|
||||
//#region $scope init
|
||||
$scope.html = {
|
||||
disableInput: true, // Hides everything and displays a loading animation
|
||||
mpQuery: "" // Text input for searching a mounting point
|
||||
};
|
||||
|
||||
$scope.objects = {
|
||||
layers: null, // Layer[]
|
||||
assets: {
|
||||
available: null, // Asset[]
|
||||
selected: null // Asset
|
||||
},
|
||||
mountingPoints: null, // MountingPoint[]
|
||||
locations: null // Location[]
|
||||
};
|
||||
|
||||
$scope.functions = {
|
||||
search: null, // function(): (function(mountingPoint: MountingPoint): boolean)
|
||||
updateView: null, // function(): void
|
||||
openPopup: null // function(mountingPoint: MountingPoint): void
|
||||
};
|
||||
|
||||
$scope.ar = {
|
||||
enabled: false, // AR mode enabled when true
|
||||
current: "" // Name of mounting point currently shown in ar
|
||||
};
|
||||
//#endregion
|
||||
|
||||
//#region Map
|
||||
const defaultView: L.LatLngBounds = new L.LatLngBounds([
|
||||
[53.0528064472226, 8.89412999153137],
|
||||
[53.056520842139, 8.90030980110168]
|
||||
]); // Default view centered on Bremen
|
||||
|
||||
const map = new L.Map("map", { attributionControl: false }).fitBounds(
|
||||
defaultView
|
||||
); // Create the map
|
||||
|
||||
try {
|
||||
var locations: Location[] = (await $http.get(
|
||||
"map/overlays.json"
|
||||
)).data.map((raw: any) => new Location(raw)); // Create map overlays
|
||||
|
||||
$scope.objects.locations = locations;
|
||||
for (let location of locations) location.addTo(map); // Add overlays to map
|
||||
map.fitBounds(locations[0].getBounds()); // Center on the first overlay
|
||||
} catch (e) {
|
||||
if (DEBUG)
|
||||
console.error(`Could not load map overlays:\n${JSON.stringify(e)}`);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const data = await dataRequest; // Wait for websocket response
|
||||
var t0 = performance.now(); // Time measurement
|
||||
|
||||
//#region Buildings
|
||||
let buildings = (data.objects as any[]).map(raw => new Building(raw));
|
||||
|
||||
let leafletFootprints = new L.GeoJSON(
|
||||
{
|
||||
type: "FeatureCollection",
|
||||
features: buildings
|
||||
.map(building => building.Footprint)
|
||||
.filter(footprint => footprint != null)
|
||||
} as GeoJSON.FeatureCollection,
|
||||
{
|
||||
onEachFeature: (feature, layer) => {
|
||||
layer.bindPopup(`<h2>${feature.properties.name}</h2>`);
|
||||
}
|
||||
}
|
||||
).addTo(map);
|
||||
//#endregion
|
||||
|
||||
//#region Layers
|
||||
let layers: Layer[] = [];
|
||||
|
||||
for (let level of data.levels) {
|
||||
let add = true;
|
||||
|
||||
for (let layer of layers) {
|
||||
if (layer.Name === level.designation) {
|
||||
// Same level name -> same layer
|
||||
|
||||
add = false;
|
||||
layer.LayerIds.push(level.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (add) {
|
||||
let temp = new Layer(level.designation);
|
||||
temp.LayerIds.push(level.id);
|
||||
layers.push(temp);
|
||||
}
|
||||
}
|
||||
|
||||
for (let layer of layers) layer.LeafletLayer.addTo(map);
|
||||
|
||||
let outdoorLayer = new Layer("outdoor");
|
||||
outdoorLayer.LayerIds = [-1];
|
||||
outdoorLayer.LeafletLayer.addTo(map);
|
||||
|
||||
let footprintLayer = new Layer("buildings");
|
||||
footprintLayer.LeafletLayer = leafletFootprints;
|
||||
footprintLayer.LeafletLayer.addTo(map);
|
||||
|
||||
layers.push(outdoorLayer, footprintLayer);
|
||||
|
||||
$scope.objects.layers = layers;
|
||||
//#endregion
|
||||
|
||||
//#region Assets
|
||||
let assets = (data.assetTypes as any[]).map(raw => new Asset(raw));
|
||||
|
||||
$scope.objects.assets.available = assets;
|
||||
$scope.objects.assets.selected = assets[0];
|
||||
//#endregion
|
||||
|
||||
//#region Mounting points
|
||||
let mountingPoints: MountingPoint[] = [];
|
||||
|
||||
for (let mountingPoint of data.mountingPoints) {
|
||||
for (let building of buildings) {
|
||||
if (building.Id == mountingPoint.objectId) {
|
||||
// Find matching building for each mounting point
|
||||
|
||||
let temp = await MountingPoint.Create(mountingPoint, building);
|
||||
|
||||
mountingPoints.push(temp);
|
||||
|
||||
for (let layer of layers) {
|
||||
if (layer.LayerIds.includes(temp.Level)) {
|
||||
// Add mounting point to correct layer
|
||||
layer.LeafletLayer.addLayer(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let layer of layers) {
|
||||
layer.LeafletLayer.addTo(map);
|
||||
// Add all layers to the map
|
||||
}
|
||||
|
||||
$scope.objects.mountingPoints = mountingPoints;
|
||||
//#endregion
|
||||
|
||||
//#region Angular functions
|
||||
/** @function */ $scope.functions.search = () => {
|
||||
return (mountingPoint: MountingPoint) => {
|
||||
/* Correct asset type? */
|
||||
let correctAssetType = false;
|
||||
|
||||
for (let id of mountingPoint.SupportedAssets) {
|
||||
if (id == $scope.objects.assets.selected.Id) {
|
||||
correctAssetType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Correct layer? */
|
||||
let correctLayer = false;
|
||||
|
||||
for (let layer of layers) {
|
||||
if (layer.LayerIds.includes(mountingPoint.Level)) {
|
||||
correctLayer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ID or Name matches query? */
|
||||
let query = $scope.html.mpQuery;
|
||||
let foundBySearch = _(query)
|
||||
? true
|
||||
: mountingPoint.Name.includes(query) ||
|
||||
mountingPoint.Id.toString().includes(query);
|
||||
|
||||
return correctAssetType && correctLayer && foundBySearch;
|
||||
};
|
||||
};
|
||||
|
||||
/** @function */ $scope.functions.updateView = () => {
|
||||
for (let layer of layers) {
|
||||
if (layer.Name === "buildings") {
|
||||
if (layer.Visible) leafletFootprints.addTo(map);
|
||||
else leafletFootprints.removeFrom(map);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let mountingPoint of layer.LeafletLayer.getLayers() as MountingPoint[]) {
|
||||
if (
|
||||
layer.Visible &&
|
||||
mountingPoint.SupportedAssets.includes(
|
||||
$scope.objects.assets.selected.Id
|
||||
)
|
||||
)
|
||||
mountingPoint.addTo(map);
|
||||
else mountingPoint.removeFrom(map);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @function */ $scope.functions.openPopup = (
|
||||
mountingPoint: MountingPoint
|
||||
) => {
|
||||
mountingPoint.openPopup();
|
||||
map.panTo(mountingPoint.getLatLng());
|
||||
};
|
||||
|
||||
/** @function */ $scope.functions.goToLocation = (
|
||||
bounds: L.LatLngBounds
|
||||
) => {
|
||||
map.fitBounds(bounds);
|
||||
};
|
||||
|
||||
$scope.functions.updateView();
|
||||
$scope.html.disableInput = false;
|
||||
$scope.$apply();
|
||||
|
||||
console.log(`Finished loading in ${performance.now() - t0}ms.`);
|
||||
});
|
||||
Reference in new issue
Block a user