Compare commits

..

4 Commits

Author SHA1 Message Date
d90230ef14 netcore 3.1 also needed in buildscript 2021-04-10 20:53:34 +02:00
26c6f75ae9 fixing github actions 2021-04-10 20:35:47 +02:00
3705d78dcd 1.3.1 Refactory is the king 2021-04-10 20:32:24 +02:00
7dc841fe5c Update Doku 2020-02-12 14:30:36 +01:00
43 changed files with 2393 additions and 1512 deletions

View File

@ -21,7 +21,7 @@ jobs:
- name: Install dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.0.100
dotnet-version: '3.1.x'
- name: Build with dotnet
run: dotnet build Lora-Map.sln --configuration Release

View File

@ -16,19 +16,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\update.sh = ..\update.sh
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot-Utils_Core", "..\Utils\Bot-Utils\Bot-Utils\Bot-Utils_Core.csproj", "{ED37370F-AE65-498D-A425-413FEE69C0A8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot-Utils", "..\Utils\Bot-Utils\Bot-Utils\Bot-Utils.csproj", "{ED37370F-AE65-498D-A425-413FEE69C0A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils_Core", "..\Utils\Utils\Utils\Utils_Core.csproj", "{E8268FE5-D6F0-4805-9BDE-9DBC9CB517FF}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "..\Utils\Utils\Utils\Utils.csproj", "{E8268FE5-D6F0-4805-9BDE-9DBC9CB517FF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils-IoT_Core", "..\Utils\Utils-IoT\Utils-IoT\Utils-IoT_Core.csproj", "{04CF6328-3976-44D3-9959-A3B1A2C5C45A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils-IoT", "..\Utils\Utils-IoT\Utils-IoT\Utils-IoT.csproj", "{04CF6328-3976-44D3-9959-A3B1A2C5C45A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorDataMqtt_Core", "..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt_Core.csproj", "{E40D29CB-B499-4FA6-AEA1-18E8CEAA911B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorDataMqtt", "..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt.csproj", "{E40D29CB-B499-4FA6-AEA1-18E8CEAA911B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "litjson_Core", "..\Librarys\litjson\litjson\litjson_Core.csproj", "{FFC66B7F-B4FB-4E42-B896-2C6772D899AA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "litjson", "..\Librarys\litjson\litjson\litjson.csproj", "{FFC66B7F-B4FB-4E42-B896-2C6772D899AA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "M2Mqtt_Core", "..\Librarys\mqtt\M2Mqtt\M2Mqtt_Core.csproj", "{00C678EE-6BAA-4FCB-AAA5-7755E65C6CC5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "M2Mqtt", "..\Librarys\mqtt\M2Mqtt\M2Mqtt.csproj", "{00C678EE-6BAA-4FCB-AAA5-7755E65C6CC5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoordinateSharp_Core", "..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp_Core.csproj", "{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoordinateSharp", "..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp.csproj", "{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Fraunhofer.Fit.IoT.LoraMap</RootNamespace>
<AssemblyName>Lora-Map</AssemblyName>
<ApplicationManifest>Adminrights.manifest</ApplicationManifest>
@ -10,15 +10,16 @@
<Description>Displays Items with Coordinates from Mqtt on a Map</Description>
<Company>Fraunhofer FIT</Company>
<PackageId>LoraMap.IoT.Fit.Fraunhofer</PackageId>
<Copyright>Copyright © Fraunhofer FIT, BlubbFish 2018 - 20.01.2020</Copyright>
<Copyright>Copyright © Fraunhofer FIT, BlubbFish 2018 - 10.04.2021</Copyright>
<Authors>BlubbFish</Authors>
<Version>1.3.0</Version>
<Version>1.3.1</Version>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://github.com/MONICA-Project/lora-map</PackageProjectUrl>
<RepositoryUrl>https://github.com/MONICA-Project/lora-map.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<NeutralLanguage>de-DE</NeutralLanguage>
<PackageReleaseNotes>
1.3.1 Refactory is the king
1.3.0 New Gateway
1.2.10 Refactoring is the thing
1.2.9 The PüMa Release
@ -43,13 +44,13 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp_Core.csproj" />
<ProjectReference Include="..\..\Librarys\litjson\litjson\litjson_Core.csproj" />
<ProjectReference Include="..\..\Librarys\mqtt\M2Mqtt\M2Mqtt_Core.csproj" />
<ProjectReference Include="..\..\Utils\Bot-Utils\Bot-Utils\Bot-Utils_Core.csproj" />
<ProjectReference Include="..\..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt_Core.csproj" />
<ProjectReference Include="..\..\Utils\Utils-IoT\Utils-IoT\Utils-IoT_Core.csproj" />
<ProjectReference Include="..\..\Utils\Utils\Utils\Utils_Core.csproj" />
<ProjectReference Include="..\..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp.csproj" />
<ProjectReference Include="..\..\Librarys\litjson\litjson\litjson.csproj" />
<ProjectReference Include="..\..\Librarys\mqtt\M2Mqtt\M2Mqtt.csproj" />
<ProjectReference Include="..\..\Utils\Bot-Utils\Bot-Utils\Bot-Utils.csproj" />
<ProjectReference Include="..\..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt.csproj" />
<ProjectReference Include="..\..\Utils\Utils-IoT\Utils-IoT\Utils-IoT.csproj" />
<ProjectReference Include="..\..\Utils\Utils\Utils\Utils.csproj" />
</ItemGroup>
<ItemGroup>
@ -57,7 +58,9 @@
<Content Include="../CONTRIBUTING.md" />
<Content Include="../LICENSE" />
<Content Include="../README.md" />
<Content Include="../map-swagger.yml" />
<Content Include="../.github/workflows/dotnetcore.yml" />
<Content Include="../doc/Manual.md" />
</ItemGroup>
<ItemGroup>
@ -70,7 +73,16 @@
<None Update="resources\admin\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\js\menu.js">
<None Update="resources\admin\js\adminmenu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\js\eximport.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\js\nameseditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\js\settings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\login.html">

View File

@ -2,17 +2,26 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using BlubbFish.Utils;
using BlubbFish.Utils.IoT.Bots;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Admin {
class AdminModel {
public delegate void AdminEvent(Object sender, EventArgs e);
#pragma warning disable 0067
public event AdminEvent NamesUpdate;
public event AdminEvent GeoUpdate;
public event AdminEvent SettingsUpdate;
#pragma warning restore 0067
private readonly SortedDictionary<String, Tuple<String, String>> datastorage = new SortedDictionary<String, Tuple<String, String>>() {
{ "name", new Tuple<String, String>("names", "NamesUpdate") },
{ "geo", new Tuple<String, String>("geo", "GeoUpdate") },
{ "setting", new Tuple<String, String>("settings", "SettingsUpdate") }
};
private readonly Dictionary<Int64, AdminSession> session = new Dictionary<Int64, AdminSession>();
private readonly Dictionary<String, String> settings;
@ -25,20 +34,39 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model.Admin {
}
}
public Boolean ParseReuqest(HttpListenerContext cont) =>
cont.Request.Url.PathAndQuery == "/admin/login" ? this.Login(cont) :
!this.CheckAuth(cont) ? false :
cont.Request.Url.PathAndQuery.StartsWith("/admin/get_json_") ? this.GetJsonFiles(cont) :
cont.Request.Url.PathAndQuery.StartsWith("/admin/set_json_") ? this.SetJsonFiles(cont) :
Webserver.SendFileResponse(cont);
public Boolean ParseReuqest(HttpListenerContext cont) {
if(cont.Request.Url.PathAndQuery == "/admin/login") {
return this.Login(cont);
}
if(!this.CheckAuth(cont)) {
return false;
}
if(cont.Request.Url.PathAndQuery.StartsWith("/admin/api/json/")) {
if(cont.Request.HttpMethod == "GET") {
if(cont.Request.Url.AbsolutePath.Length > 16) {
String parts = cont.Request.Url.AbsolutePath[16..];
Dictionary<String, JsonData> ret = new Dictionary<String, JsonData>();
foreach(String part in parts.Split(",")) {
if(this.datastorage.ContainsKey(part)) {
ret.Add(part, JsonMapper.ToObject(File.ReadAllText("json/" + this.datastorage[part].Item1 + ".json")));
}
}
Console.WriteLine("200 - Send names.json " + cont.Request.Url.PathAndQuery);
return Webserver.SendJsonResponse(ret, cont);
}
} else if(cont.Request.HttpMethod == "PUT") {
if(cont.Request.Url.AbsolutePath.Length > 16) {
String part = cont.Request.Url.AbsolutePath[16..];
if(this.datastorage.ContainsKey(part)) {
return this.SetJsonFile(cont, "json/" + this.datastorage[part].Item1 + ".json", this.datastorage[part].Item2);
}
}
}
}
return Webserver.SendFileResponse(cont);
}
private Boolean SetJsonFiles(HttpListenerContext cont) =>
cont.Request.Url.PathAndQuery == "/admin/set_json_names" ? this.SetJsonFile(cont, "names.json", this.NamesUpdate) :
cont.Request.Url.PathAndQuery == "/admin/set_json_geo" ? this.SetJsonFile(cont, "geo.json", this.GeoUpdate) :
cont.Request.Url.PathAndQuery == "/admin/set_json_settings" ? this.SetJsonFile(cont, "settings.json", this.SettingsUpdate) :
false;
private Boolean SetJsonFile(HttpListenerContext cont, String filename, AdminEvent updatenotifier) {
private Boolean SetJsonFile(HttpListenerContext cont, String filename, String updatenotifier) {
StreamReader reader = new StreamReader(cont.Request.InputStream, cont.Request.ContentEncoding);
String rawData = reader.ReadToEnd();
cont.Request.InputStream.Close();
@ -50,24 +78,9 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model.Admin {
cont.Response.StatusCode = 501;
return false;
}
File.WriteAllText("json/"+ filename, rawData);
Console.WriteLine("200 - Post " + filename + " " + cont.Request.Url.PathAndQuery);
updatenotifier?.Invoke(this, new EventArgs());
return true;
}
private Boolean GetJsonFiles(HttpListenerContext cont) =>
cont.Request.Url.PathAndQuery == "/admin/get_json_names" ? this.GetJsonFile(cont, "names.json") :
cont.Request.Url.PathAndQuery == "/admin/get_json_geo" ? this.GetJsonFile(cont, "geo.json") :
cont.Request.Url.PathAndQuery == "/admin/get_json_settings" ? this.GetJsonFile(cont, "settings.json") :
false;
private Boolean GetJsonFile(HttpListenerContext cont, String filename) {
String file = File.ReadAllText("json/" + filename);
Byte[] buf = Encoding.UTF8.GetBytes(file);
cont.Response.ContentLength64 = buf.Length;
cont.Response.OutputStream.Write(buf, 0, buf.Length);
Console.WriteLine("200 - Send names.json " + cont.Request.Url.PathAndQuery);
File.WriteAllText(filename, rawData);
Console.WriteLine("200 - PUT " + filename + " " + cont.Request.Url.PathAndQuery);
this.GetEvent<AdminEvent>(updatenotifier)?.Invoke(this, new EventArgs());
return true;
}

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Admin {
class AdminSession {

View File

@ -2,17 +2,15 @@
using System;
using System.Globalization;
namespace Fraunhofer.Fit.IoT.LoraMap.Model
{
class Camera
{
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Camera {
public class CameraCounter {
public DateTime Lastcameradata { get; private set; }
public String Name { get; private set; }
public Int32 Total { get; private set; }
public Int32 Incoming { get; private set; }
public Int32 Outgoing { get; private set; }
public Camera(JsonData json) => this.Update(json);
public CameraCounter(JsonData json) => this.Update(json);
internal static String GetId(JsonData json) => (String)json["camera_id"];

View File

@ -2,17 +2,15 @@
using System;
using System.Globalization;
namespace Fraunhofer.Fit.IoT.LoraMap.Model
{
public class Crowd
{
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Camera {
public class CameraDensity {
public Int32 DensityCount { get; private set; }
public DateTime TimeStamp { get; private set; }
public Double AverageFlowMagnitude { get; private set; }
public Double AverageFlowDirection { get; private set; }
public DateTime LastUpdate { get; private set; }
public Crowd(JsonData json) => this.Update(json);
public CameraDensity(JsonData json) => this.Update(json);
public static Boolean CheckJsonCrowdDensityLocal(JsonData json) => json.ContainsKey("camera_ids") && json["camera_ids"].IsArray && json["camera_ids"].Count == 1 &&
json.ContainsKey("density_map") && json["density_map"].IsArray &&

View File

@ -2,9 +2,9 @@
using System;
using System.Globalization;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public class Fight {
public Fight(JsonData json) => this.Update(json);
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Camera {
public class CameraFights {
public CameraFights(JsonData json) => this.Update(json);
public DateTime LastUpdate { get; private set; }
public DateTime TimeStamp { get; private set; }

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Text;
using BlubbFish.Utils;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Camera {
public class CameraModel : OwnSingeton<CameraModel> {
private readonly Object lockFight = new Object();
private readonly Object lockCount = new Object();
private readonly Object lockDensy = new Object();
public SortedDictionary<String, CameraCounter> Counter {
get; private set;
}
public SortedDictionary<String, CameraDensity> Density {
get; private set;
}
public SortedDictionary<String, CameraFights> Fights {
get; private set;
}
protected CameraModel() {
this.Counter = new SortedDictionary<String, CameraCounter>();
this.Density = new SortedDictionary<String, CameraDensity>();
this.Fights = new SortedDictionary<String, CameraFights>();
}
public void ParseMqttJson(JsonData mqtt, String from) {
if(from.Contains("camera/count") && CameraCounter.CheckJson(mqtt)) {
String cameraid = CameraCounter.GetId(mqtt);
lock(this.lockCount) {
if(this.Counter.ContainsKey(cameraid)) {
this.Counter[cameraid].Update(mqtt);
} else {
this.Counter.Add(cameraid, new CameraCounter(mqtt));
}
}
} else if((from.Contains("sfn/crowd_density_local") || from.Contains("camera/crowd")) && CameraDensity.CheckJsonCrowdDensityLocal(mqtt) || from.Contains("sfn/flow") && CameraDensity.CheckJsonFlow(mqtt)) {
String cameraid = CameraDensity.GetId(mqtt);
lock(this.lockDensy) {
if(this.Density.ContainsKey(cameraid)) {
this.Density[cameraid].Update(mqtt);
} else {
this.Density.Add(cameraid, new CameraDensity(mqtt));
}
}
} else if((from.Contains("camera/fighting_detection") || from.Contains("sfn/fighting_detection")) && CameraFights.CheckJsonFightingDetection(mqtt)) {
String cameraid = CameraFights.GetId(mqtt);
lock(this.lockFight) {
if(this.Fights.ContainsKey(cameraid)) {
this.Fights[cameraid].Update(mqtt);
} else {
this.Fights.Add(cameraid, new CameraFights(mqtt));
}
}
}
}
}
}

View File

@ -1,123 +0,0 @@
using System;
using System.IO;
using System.Xml;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model
{
class Marker {
private readonly XmlDocument svg = new XmlDocument();
public Marker(String hash) {
this.svg.LoadXml(File.ReadAllText("resources/icons/marker/Marker.svg"));
this.ParseParams(hash);
}
public static String ParseMarkerConfig(JsonData json, String name) {
String ret = "icons/marker/Marker.svg";
if(json.ContainsKey("person") && json["person"].IsObject) {
ret += "?icon=person";
if(json["person"].ContainsKey("org") && json["person"]["org"].IsString) {
ret += "&person-org=" + (String)json["person"]["org"];
}
if(json["person"].ContainsKey("funct") && json["person"]["funct"].IsString) {
ret += "&person-funct=" + (String)json["person"]["funct"];
}
if(json["person"].ContainsKey("rang") && json["person"]["rang"].IsString) {
ret += "&person-rang=" + (String)json["person"]["rang"];
}
if(json["person"].ContainsKey("text") && json["person"]["text"].IsString) {
ret += "&person-text=" + (String)json["person"]["text"];
}
if(json["person"].ContainsKey("typ") && json["person"]["typ"].IsArray) {
foreach(JsonData item in json["person"]["typ"]) {
if(item.IsString) {
ret += "&person-typ=" + (String)item;
}
}
}
}
ret += ret.Contains("?") ? "&name=" + name : "?name=" + name;
return ret;
}
private void ParseParams(String hash) {
String[] parts = hash.Split('&');
foreach(String part in parts) {
String[] keyvalue = part.Split('=');
if(keyvalue.Length == 2) {
switch(keyvalue[0].ToLower()) {
case "name":
XmlNodeList xmlname = this.svg.DocumentElement.SelectNodes("//*[local-name()='tspan'][@id='marker-name-text']");
if(xmlname.Count == 1) {
xmlname.Item(0).InnerText = keyvalue[1];
}
break;
case "marker-bg":
if(keyvalue[1].ToLower() == "hidden") {
XmlNodeList markerbg = this.svg.DocumentElement.SelectNodes("//*[local-name()='defs'][@id='global-def']");
if(markerbg.Count == 1) {
markerbg[0].InnerXml += "<style type=\"text/css\">#marker-bg {display: none;}#marker-name {display: none;}</style>";
}
XmlNodeList root = this.svg.DocumentElement.SelectNodes("//*[local-name()='svg']");
if(root.Count == 1) {
foreach(XmlAttribute item in root[0].Attributes) {
if(item.Name.ToLower() == "height") {
item.Value = "38px";
}
if(item.Name.ToLower() == "width") {
item.Value = "40px";
}
if(item.Name.ToLower() == "viewbox") {
item.Value = "8 35 70 100";
}
}
}
}
break;
case "icon":
if(keyvalue[1].ToLower() == "person") {
XmlNodeList xmlicon = this.svg.DocumentElement.SelectNodes("//*[local-name()='defs'][@id='global-def']");
if (xmlicon.Count == 1) {
xmlicon.Item(0).InnerXml += "<style type=\"text/css\">#marker-icon #person { display: inline; }</style>";
}
}
break;
case "person-org":
XmlNodeList xmlpersonorg = this.svg.DocumentElement.SelectNodes("//*[local-name()='defs'][@id='people-def']");
if (xmlpersonorg.Count == 1) {
xmlpersonorg.Item(0).InnerXml += "<style type=\"text/css\">#person-layer-org #person-layer-org-"+ keyvalue[1] + " { display: inline; }</style>";
}
break;
case "person-funct":
XmlNodeList xmlpersonfunct = this.svg.DocumentElement.SelectNodes("//*[local-name()='defs'][@id='people-def']");
if (xmlpersonfunct.Count == 1) {
xmlpersonfunct.Item(0).InnerXml += "<style type=\"text/css\">#person-layer-funct #person-layer-funct-" + keyvalue[1] + " { display: inline; }</style>";
}
break;
case "person-rang":
XmlNodeList xmlpersonrang = this.svg.DocumentElement.SelectNodes("//*[local-name()='defs'][@id='people-def']");
if (xmlpersonrang.Count == 1) {
xmlpersonrang.Item(0).InnerXml += "<style type=\"text/css\">#person-layer-rang #person-layer-rang-" + keyvalue[1] + " { display: inline; }</style>";
}
break;
case "person-text":
XmlNodeList xmlpersontext = this.svg.DocumentElement.SelectNodes("//*[local-name()='tspan'][@id='person-layer-typ-text']");
if (xmlpersontext.Count == 1) {
xmlpersontext.Item(0).InnerText = keyvalue[1];
}
break;
case "person-typ":
XmlNodeList xmlpersontyp = this.svg.DocumentElement.SelectNodes("//*[local-name()='defs'][@id='people-def']");
if(xmlpersontyp.Count == 1) {
xmlpersontyp.Item(0).InnerXml += "<style type=\"text/css\">#person-layer-typ #person-layer-typ-" + keyvalue[1] + " { display: inline; }</style>";
}
break;
}
}
}
}
public override String ToString() => this.svg.OuterXml;
}
}

View File

@ -3,13 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public class AlarmItem : PositionItem {
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Position {
public class PositionAlarm : PositionItem {
private readonly SortedDictionary<DateTime, String> buttonhistory = new SortedDictionary<DateTime, String>();
public List<DateTime> ButtonPressed => this.buttonhistory.Keys.ToList();
public AlarmItem(JsonData json) : base(json, null) {
public PositionAlarm(JsonData json) : base(json, null) {
}
public override void Update(JsonData json) {

View File

@ -1,8 +1,11 @@
using System;
using System.Globalization;
using Fraunhofer.Fit.IoT.LoraMap.Model.Svg;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Position {
public class PositionItem {
private Double _lastLat = 0;
private Double _lastLon = 0;
@ -22,6 +25,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public Double Height { get; private set; }
public String Name { get; private set; }
public String Icon { get; private set; }
public String MenuIcon { get; private set; }
public String Group { get; private set; }
public PositionItem(JsonData json, JsonData marker) {
@ -32,7 +36,9 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public void UpdateMarker(JsonData marker, String id) {
if(marker != null && marker.ContainsKey(id)) {
this.Name = marker[id].ContainsKey("name") && marker[id]["name"].IsString ? (String)marker[id]["name"] : id;
this.Icon = marker[id].ContainsKey("marker.svg") && marker[id]["marker.svg"].IsObject ? Marker.ParseMarkerConfig(marker[id]["marker.svg"], this.Name) : marker[id].ContainsKey("icon") && marker[id]["icon"].IsString ? (String)marker[id]["icon"] : null;
Tuple<String, String> icons = this.ParseIconConfig(marker[id]);
this.Icon = icons.Item1;
this.MenuIcon = icons.Item2;
this.Group = marker[id].ContainsKey("Group") && marker[id]["Group"].IsString ? (String)marker[id]["Group"] : "no";
} else {
this.Name = id;
@ -40,6 +46,21 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
this.Group = null;
}
}
private Tuple<String, String> ParseIconConfig(JsonData marker) {
String icon = null;
String menu = null;
if(marker.ContainsKey("marker.svg") && marker["marker.svg"].IsObject) {
icon = SVGMarker.ParseConfig(marker["marker.svg"], this.Name);
if(marker["marker.svg"].ContainsKey("person") && marker["marker.svg"]["person"].IsObject) {
menu = SVGPerson.ParseConfig(marker["marker.svg"]["person"]);
}
} else if(marker.ContainsKey("icon") && marker["icon"].IsString) {
icon = (String)marker["icon"];
}
return new Tuple<String, String>(icon, menu);
}
public static Boolean CheckJson(JsonData json) =>
json.ContainsKey("BatteryLevel") && (json["BatteryLevel"].IsDouble || json["BatteryLevel"].IsInt)
&& json.ContainsKey("Gps") && json["Gps"].IsObject

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.IO;
using BlubbFish.Utils;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Position {
public class PositionModel : OwnSingeton<PositionModel> {
private readonly Object lockData = new Object();
private readonly Object lockPanic = new Object();
private JsonData marker;
public SortedDictionary<String, PositionItem> Positions {
get; private set;
}
public SortedDictionary<String, PositionAlarm> Alarms {
get; private set;
}
protected PositionModel() {
this.Positions = new SortedDictionary<String, PositionItem>();
this.Alarms = new SortedDictionary<String, PositionAlarm>();
this.CheckJsonFile();
this.marker = JsonMapper.ToObject(File.ReadAllText("json/names.json"));
}
public void ParseMqttJson(JsonData mqtt, String from) {
if((from.Contains("lora/data") || from.Contains("lora/panic")) && PositionItem.CheckJson(mqtt)) {
String name = PositionItem.GetId(mqtt);
lock(this.lockData) {
if(this.Positions.ContainsKey(name)) {
this.Positions[name].Update(mqtt);
} else {
this.Positions.Add(name, new PositionItem(mqtt, this.marker));
}
}
if(from.Contains("lora/panic")) {
lock(this.lockPanic) {
if(this.Alarms.ContainsKey(name)) {
this.Alarms[name].Update(mqtt);
} else {
this.Alarms.Add(name, new PositionAlarm(mqtt));
}
}
Console.WriteLine("PANIC erhalten!");
}
Console.WriteLine("Koordinate erhalten!");
}
}
public void ReloadNames(Object sender, EventArgs e) {
this.CheckJsonFile();
this.marker = JsonMapper.ToObject(File.ReadAllText("json/names.json"));
foreach(KeyValuePair<String, PositionItem> item in this.Positions) {
item.Value.UpdateMarker(this.marker, item.Key);
}
Console.WriteLine("Namen und Icons aktualisiert!");
}
private void CheckJsonFile() {
if(!Directory.Exists("json")) {
_ = Directory.CreateDirectory("json");
}
if(!File.Exists("json/names.json")) {
File.WriteAllText("json/names.json", "{}");
}
}
}
}

View File

@ -3,8 +3,8 @@ using System.Globalization;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
class EnviromentData {
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Sensor {
public class SensorEnviromentData {
public String Name { get; private set; }
public Double Rssi { get; private set; }
public Double Snr { get; private set; }
@ -13,7 +13,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public Double Windspeed { get; private set; }
public DateTime Lorarecievedtime { get; private set; }
public EnviromentData(JsonData json) => this.Update(json);
public SensorEnviromentData(JsonData json) => this.Update(json);
public void Update(JsonData json) {
this.Name = GetId(json);

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Text;
using BlubbFish.Utils;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Sensor {
public class SensorModel : OwnSingeton<SensorModel> {
private readonly Object lockSensor = new Object();
public SortedDictionary<String, SensorEnviromentData> Enviroments {
get; private set;
}
public SensorWeather Weather {
get; private set;
}
protected SensorModel() {
this.Enviroments = new SortedDictionary<String, SensorEnviromentData>();
this.Weather = new SensorWeather();
}
public void ParseMqttJson(JsonData mqtt, String from) {
if(from.Contains("lora/sensor") && SensorEnviromentData.CheckJson(mqtt)) {
String sensorid = SensorEnviromentData.GetId(mqtt);
lock(this.lockSensor) {
if(this.Enviroments.ContainsKey(sensorid)) {
this.Enviroments[sensorid].Update(mqtt);
} else {
this.Enviroments.Add(sensorid, new SensorEnviromentData(mqtt));
}
}
Console.WriteLine("Umweltdaten erhalten!");
}
}
public void Dispose() => this.Weather.Dispose();
}
}

View File

@ -6,21 +6,18 @@ using Fraunhofer.Fit.IoT.LoraMap.Lib;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public class WeatherWarnings {
private readonly Settings settings;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Sensor {
public class SensorWeather {
private Thread bgthread;
private Boolean backgroundrunnerAlive;
private readonly WebRequests webrequests = new WebRequests();
public List<Warning> Warnungen { get; private set; }
public WeatherWarnings(Settings settings) {
this.settings = settings;
this.StartBackgroundThread();
}
public SensorWeather() => this.StartBackgroundThread();
private void StartBackgroundThread() {
this.Warnungen = new List<Warning>();
this.bgthread = new Thread(this.BackGroundRunner);
this.backgroundrunnerAlive = true;
this.bgthread.Start();
@ -29,7 +26,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
private void BackGroundRunner() {
while(this.backgroundrunnerAlive) {
List<Warning> ret = new List<Warning>();
foreach(Int32 item in this.settings.GetWeatherCellIds()) {
foreach(Int32 item in Settings.Instance.Internal.WeatherCellIDs) {
try {
JsonData json = this.webrequests.GetJson("https://maps.dwd.de/geoserver/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typeName=dwd:Warnungen_Gemeinden&outputFormat=application/json&cql_filter=WARNCELLID=" + item);
if (json.ContainsKey("features") && json["features"].IsArray && json["features"].Count > 0) {
@ -50,10 +47,10 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
}
}
internal void Dispose() {
public void Dispose() {
try {
this.backgroundrunnerAlive = false;
while (this.bgthread.IsAlive) {
while (this.bgthread != null && this.bgthread.IsAlive) {
Thread.Sleep(10);
}
this.bgthread = null;

View File

@ -1,78 +1,88 @@
using CoordinateSharp;
using BlubbFish.Utils;
using CoordinateSharp;
using LitJson;
using System;
using System.Collections.Generic;
using System.IO;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public class Settings {
private readonly List<Int32> weatherCellIDs = new List<Int32>();
public class Settings : OwnSingeton<Settings> {
private Int32 gridradius;
public Double Startloclat { get; private set; }
public Double Startloclon { get; private set; }
public Dictionary<String, List<Dictionary<String, List<Double>>>> Grid { get; private set; }
public Dictionary<String, Fight> FightDedection { get; private set; }
public Dictionary<String, Density> DensityArea { get; private set; }
public Dictionary<String, Sensor> Sensors { get; private set; }
public PublicSettings External {
get; set;
}
public Settings() => this.ParseJson();
public void AdminModelUpdateSettings(Object sender, EventArgs e) => this.ParseJson();
public PrivateSettings Internal {
get; set;
}
private void ParseJson() {
protected Settings() {
this.External = new PublicSettings();
this.Internal = new PrivateSettings();
this.ParseSettingsJson();
this.ParseGeoJson();
}
public void ReloadSettings(Object sender, EventArgs e) => this.ParseSettingsJson();
public void ReloadGeo(Object sender, EventArgs e) => this.ParseGeoJson();
private void ParseSettingsJson() {
this.CheckJsonFiles();
JsonData json = JsonMapper.ToObject(File.ReadAllText("json/settings.json"));
if(json.ContainsKey("StartPos") && json["StartPos"].IsObject && json["StartPos"].ContainsKey("lat") && json["StartPos"]["lat"].IsDouble && json["StartPos"].ContainsKey("lon") && json["StartPos"]["lon"].IsDouble) {
this.Startloclat = (Double)json["StartPos"]["lat"];
this.Startloclon = (Double)json["StartPos"]["lon"];
if(json.ContainsKey("StartPos") && json["StartPos"].IsObject && json["StartPos"].ContainsKey("lat") && (json["StartPos"]["lat"].IsDouble || json["StartPos"]["lat"].IsInt) && json["StartPos"].ContainsKey("lon") && (json["StartPos"]["lon"].IsDouble || json["StartPos"]["lon"].IsInt)) {
this.External.Startloclat = Double.Parse(json["StartPos"]["lat"].ToString());
this.External.Startloclon = Double.Parse(json["StartPos"]["lon"].ToString());
} else {
this.Startloclat = 0;
this.Startloclon = 0;
this.External.Startloclat = 0;
this.External.Startloclon = 0;
}
this.weatherCellIDs.Clear();
this.Internal.WeatherCellIDs.Clear();
if(json.ContainsKey("CellIds") && json["CellIds"].IsArray && json["CellIds"].Count > 0) {
foreach (JsonData item in json["CellIds"]) {
foreach(JsonData item in json["CellIds"]) {
if(Int32.TryParse(item.ToString(), out Int32 cellid)) {
this.weatherCellIDs.Add(cellid);
this.Internal.WeatherCellIDs.Add(cellid);
}
}
}
if(json.ContainsKey("FightDedection") && json["FightDedection"].IsObject) {
Dictionary<String, Fight> fights = new Dictionary<String, Fight>();
foreach (KeyValuePair<String, JsonData> entry in json["FightDedection"]) {
foreach(KeyValuePair<String, JsonData> entry in json["FightDedection"]) {
Fight fight = new Fight {
Polygon = new List<List<Double>>()
};
if(entry.Value.ContainsKey("Poly") && entry.Value["Poly"].IsArray) {
foreach (JsonData coord in entry.Value["Poly"]) {
foreach(JsonData coord in entry.Value["Poly"]) {
List<Double> coords = new List<Double>();
if (coord.ContainsKey("Lat") && coord["Lat"].IsDouble && coord.ContainsKey("Lon") && coord["Lon"].IsDouble) {
if(coord.ContainsKey("Lat") && coord["Lat"].IsDouble && coord.ContainsKey("Lon") && coord["Lon"].IsDouble) {
coords.Add((Double)coord["Lat"]);
coords.Add((Double)coord["Lon"]);
}
fight.Polygon.Add(coords);
}
}
if (entry.Value.ContainsKey("Level") && entry.Value["Level"].IsDouble) {
if(entry.Value.ContainsKey("Level") && entry.Value["Level"].IsDouble) {
fight.Level = (Double)entry.Value["Level"];
}
if (entry.Value.ContainsKey("Alias") && entry.Value["Alias"].IsString) {
if(entry.Value.ContainsKey("Alias") && entry.Value["Alias"].IsString) {
fight.Alias = (String)entry.Value["Alias"];
}
fights.Add(entry.Key, fight);
}
this.FightDedection = fights;
this.External.FightDedection = fights;
}
if(json.ContainsKey("CrwodDensity") && json["CrwodDensity"].IsObject) {
Dictionary<String, Density> densitys = new Dictionary<String, Density>();
foreach (KeyValuePair<String, JsonData> entry in json["CrwodDensity"]) {
foreach(KeyValuePair<String, JsonData> entry in json["CrwodDensity"]) {
Density density = new Density {
Polygon = new List<List<Double>>()
};
if (entry.Value.ContainsKey("Poly") && entry.Value["Poly"].IsArray) {
foreach (JsonData coord in entry.Value["Poly"]) {
if(entry.Value.ContainsKey("Poly") && entry.Value["Poly"].IsArray) {
foreach(JsonData coord in entry.Value["Poly"]) {
List<Double> coords = new List<Double>();
if (coord.ContainsKey("Lat") && coord["Lat"].IsDouble && coord.ContainsKey("Lon") && coord["Lon"].IsDouble) {
if(coord.ContainsKey("Lat") && coord["Lat"].IsDouble && coord.ContainsKey("Lon") && coord["Lon"].IsDouble) {
coords.Add((Double)coord["Lat"]);
coords.Add((Double)coord["Lon"]);
}
@ -87,7 +97,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
}
densitys.Add(entry.Key, density);
}
this.DensityArea = densitys;
this.External.DensityArea = densitys;
}
if(json.ContainsKey("Sensors") && json["Sensors"].IsObject) {
Dictionary<String, Sensor> sensors = new Dictionary<String, Sensor>();
@ -112,31 +122,37 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
sensors.Add(entry.Key, sensor);
}
}
this.Sensors = sensors;
this.External.Sensors = sensors;
}
this.gridradius = json.ContainsKey("GridRadius") && json["GridRadius"].IsInt && this.Startloclat != 0 && this.Startloclon != 0 ? (Int32)json["GridRadius"] : 0;
this.gridradius = json.ContainsKey("GridRadius") && json["GridRadius"].IsInt && this.External.Startloclat != 0 && this.External.Startloclon != 0 ? (Int32)json["GridRadius"] : 0;
this.GenerateGrid();
this.FindMapLayer();
}
private void ParseGeoJson() {
this.CheckJsonFiles();
this.External.GeoLayer = JsonMapper.ToObject(File.ReadAllText("json/geo.json"));
}
private void GenerateGrid() {
this.Grid = new Dictionary<String, List<Dictionary<String, List<Double>>>> {
this.External.Grid = new Dictionary<String, List<Dictionary<String, List<Double>>>> {
{ "Major", new List<Dictionary<String, List<Double>>>() },
{ "Minor", new List<Dictionary<String, List<Double>>>() }
};
if (this.Startloclat == 0 || this.Startloclon == 0 || this.gridradius == 0) {
if(this.External.Startloclat == 0 || this.External.Startloclon == 0 || this.gridradius == 0) {
return;
}
MilitaryGridReferenceSystem start = new Coordinate(this.Startloclat, this.Startloclon).MGRS;
MilitaryGridReferenceSystem start = new Coordinate(this.External.Startloclat, this.External.Startloclon).MGRS;
Double left = start.Easting - this.gridradius - (start.Easting - this.gridradius) % 100;
Double bottom = start.Northing - this.gridradius - (start.Northing - this.gridradius) % 100;
Double right = start.Easting + this.gridradius + (100 - (start.Easting + this.gridradius) % 100);
Double top = start.Northing + this.gridradius + (100 - (start.Northing + this.gridradius) % 100);
for (Double i = left; i <= right; i += 50) {
for(Double i = left; i <= right; i += 50) {
Coordinate TopLeft = MilitaryGridReferenceSystem.MGRStoLatLong(new MilitaryGridReferenceSystem(start.LatZone, start.LongZone, start.Digraph, i, top));
Coordinate BottomLeft = MilitaryGridReferenceSystem.MGRStoLatLong(new MilitaryGridReferenceSystem(start.LatZone, start.LongZone, start.Digraph, i, bottom));
if(i%100 == 0) {
this.Grid["Major"].Add(new Dictionary<String, List<Double>> {
if(i % 100 == 0) {
this.External.Grid["Major"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
TopLeft.Latitude.DecimalDegree,
TopLeft.Longitude.DecimalDegree
@ -149,7 +165,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
}
});
} else {
this.Grid["Minor"].Add(new Dictionary<String, List<Double>> {
this.External.Grid["Minor"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
TopLeft.Latitude.DecimalDegree,
TopLeft.Longitude.DecimalDegree
@ -163,11 +179,11 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
});
}
}
for (Double i = bottom; i <= top; i += 50) {
for(Double i = bottom; i <= top; i += 50) {
Coordinate BottomLeft = MilitaryGridReferenceSystem.MGRStoLatLong(new MilitaryGridReferenceSystem(start.LatZone, start.LongZone, start.Digraph, left, i));
Coordinate BottomRight = MilitaryGridReferenceSystem.MGRStoLatLong(new MilitaryGridReferenceSystem(start.LatZone, start.LongZone, start.Digraph, right, i));
if (i % 100 == 0) {
this.Grid["Major"].Add(new Dictionary<String, List<Double>> {
if(i % 100 == 0) {
this.External.Grid["Major"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
BottomLeft.Latitude.DecimalDegree,
BottomLeft.Longitude.DecimalDegree
@ -180,7 +196,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
}
});
} else {
this.Grid["Minor"].Add(new Dictionary<String, List<Double>> {
this.External.Grid["Minor"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
BottomLeft.Latitude.DecimalDegree,
BottomLeft.Longitude.DecimalDegree
@ -196,24 +212,117 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
}
}
public List<Int32> GetWeatherCellIds() => this.weatherCellIDs;
private void FindMapLayer() {
this.External.Layers = new Dictionary<String, Dictionary<String, Object>> {
{ "online", new Dictionary<String, Object>() {
{ "title", "Online Map" },
{ "url", "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" },
{ "attribution", "&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors" },
{ "minZoom", 1 },
{ "maxZoom", 19 }
} }
};
if(Directory.Exists("resources" + Path.DirectorySeparatorChar + "maps")) {
String[] dirs = Directory.GetDirectories("resources" + Path.DirectorySeparatorChar + "maps");
foreach(String dir in dirs) {
if(File.Exists(dir + Path.DirectorySeparatorChar + "tiles.json")) {
try {
JsonData map = JsonMapper.ToObject(File.ReadAllText(dir + Path.DirectorySeparatorChar + "tiles.json"));
Dictionary<String, Object> entry = new Dictionary<String, Object> {
{ "title", (String)map["name"] },
{ "url", (String)map["tiles"][0] },
{ "attribution", (String)map["attribution"] },
{ "minZoom", (Int32)map["minzoom"] },
{ "maxZoom", (Int32)map["maxzoom"] },
{ "bounds", new Dictionary<String, Object>() {
{ "corner1", new Double[] { (Double)map["bounds"][0], (Double)map["bounds"][1] } },
{ "corner2", new Double[] { (Double)map["bounds"][2], (Double)map["bounds"][3] } }
} }
};
this.External.Layers.Add(dir[(("resources" + Path.DirectorySeparatorChar + "maps").Length + 1)..], entry);
} catch { }
}
}
}
}
private void CheckJsonFiles() {
if(!Directory.Exists("json")) {
_ = Directory.CreateDirectory("json");
}
if(!File.Exists("json/settings.json")) {
File.WriteAllText("json/settings.json", "{}");
}
if(!File.Exists("json/geo.json")) {
File.WriteAllText("json/geo.json", "{}");
}
}
public struct Density {
public List<List<Double>> Polygon { get; set; }
public Int32 Maximum { get; set; }
public String Alias { get; set; }
public List<List<Double>> Polygon {
get; set;
}
public Int32 Maximum {
get; set;
}
public String Alias {
get; set;
}
}
public struct Fight {
public List<List<Double>> Polygon { get; set; }
public Double Level { get; set; }
public String Alias { get; set; }
public List<List<Double>> Polygon {
get; set;
}
public Double Level {
get; set;
}
public String Alias {
get; set;
}
}
public struct Sensor {
public List<Double> Coordinates { get; set; }
public Double Level { get; set; }
public String Alias { get; set; }
public List<Double> Coordinates {
get; set;
}
public Double Level {
get; set;
}
public String Alias {
get; set;
}
}
public class PublicSettings {
public Double Startloclat {
get; set;
} = 0;
public Double Startloclon {
get; set;
} = 0;
public Dictionary<String, List<Dictionary<String, List<Double>>>> Grid {
get; set;
} = new Dictionary<String, List<Dictionary<String, List<Double>>>>();
public Dictionary<String, Fight> FightDedection {
get; set;
} = new Dictionary<String, Fight>();
public Dictionary<String, Density> DensityArea {
get; set;
} = new Dictionary<String, Density>();
public Dictionary<String, Sensor> Sensors {
get; set;
} = new Dictionary<String, Sensor>();
public Dictionary<String, Dictionary<String, Object>> Layers {
get; set;
} = new Dictionary<String, Dictionary<String, Object>>();
public JsonData GeoLayer {
get; set;
}
}
public class PrivateSettings {
public List<Int32> WeatherCellIDs { get; set; } = new List<Int32>();
}
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Svg {
public abstract class SVGFile {
protected String query;
protected Double width;
protected Double height;
protected List<Double> viewbox;
private readonly Boolean withSvgHeader;
protected List<String> css = new List<String>();
public SVGFile(String query, Double width, Double heigt, List<Double> viewbox, Boolean withSvGHeader = true) {
this.query = query;
this.width = width;
this.height = heigt;
this.viewbox = viewbox;
this.withSvgHeader = withSvGHeader;
this.ParseParams();
#region add css font
if(withSvGHeader) {
this.css.Add("@font-face {\nfont-family: DIN1451;\nsrc: url(data:application/font-woff;charset=utf-8;base64,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) format('woff');\n}");
}
#endregion
}
protected static String DictionaryConfigToString(Dictionary<String, String> config) {
String query = "";
if(config.Count > 0) {
query += "?";
List<String> queryparts = new List<String>();
foreach(KeyValuePair<String, String> item in config) {
queryparts.Add(HttpUtility.UrlEncode(item.Key) + "=" + HttpUtility.UrlEncode(item.Value));
}
query += String.Join("&", queryparts);
}
return query;
}
protected abstract void ParseParams();
protected abstract String DrawSvgContent();
public override String ToString() {
String svg = "";
if(this.withSvgHeader) {
svg += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
svg += $"<svg width=\"{this.width.ToString(CultureInfo.InvariantCulture)}mm\" height=\"{this.height.ToString(CultureInfo.InvariantCulture)}mm\" viewBox=\"";
foreach(Double item in this.viewbox) {
svg += item.ToString(CultureInfo.InvariantCulture) + " ";
}
svg += "\" preserveAspectRatio=\"xMinYMin slice\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
}
if(this.css.Count > 0) {
svg += "<defs><style type=\"text/css\">\n";
foreach(String item in this.css) {
svg += item + "\n";
}
svg += "</style></defs>\n";
}
svg += this.DrawSvgContent();
if(this.withSvgHeader) {
svg += "</svg>\n";
}
return svg;
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Svg {
public class SVGMarker : SVGFile {
private String name;
private String icon;
public SVGMarker(String query) : base(query, 86, 121.25, new List<Double>() { 0, 0, 86, 121.25 }) => this.css.Add("#marker-name tspan {\n font-size: 20px;\n font-family: DIN1451;\n}");
public static String ParseConfig(JsonData json, String name) => "api/svg/marker.svg" + DictionaryConfigToString(GenerateConfig(json, name));
protected override void ParseParams() {
String[] parts = this.query.Split('&');
foreach(String part in parts) {
String[] keyvalue = part.Split('=');
if(keyvalue.Length == 2) {
switch(keyvalue[0].ToLower()) {
case "name":
this.name = keyvalue[1];
break;
case "icon":
this.icon = keyvalue[1].ToLower();
break;
}
}
}
}
public static Dictionary<String, String> GenerateConfig(JsonData json, String name) {
Dictionary<String, String> config = new Dictionary<String, String>();
if(name != "") {
config.Add("name", name);
}
if(json.ContainsKey("person") && json["person"].IsObject) {
config.Add("icon", "person");
Dictionary<String, String> personconfig = SVGPerson.GenerateConfig(json["person"]);
personconfig.ToList().ForEach(x => config.Add(x.Key, x.Value));
}
return config;
}
protected override String DrawSvgContent() {
String svg = "<g inkscape:groupmode=\"layer\" id=\"marker-bg\" inkscape:label=\"Marker\">\n";
svg += "<rect style=\"fill:#ffffff;stroke:#000000;stroke-width:1.5px;\" width=\"82.5\" height=\"110\" x=\"2\" y=\"0.75\" />\n";
svg += "<path style=\"stroke:#000000;stroke-width:1.5px;\" d=\"m 2,110.75 0,8.5\" />\n";
svg += "<circle style=\"fill:#000000;\" cx=\"2\" cy=\"119.25\" r=\"2\" />\n";
svg += "</g>\n";
svg += "<g inkscape:groupmode=\"layer\" id=\"marker-name\" inkscape:label=\"Name\">\n";
svg += $"<text><tspan x=\"5\" y=\"20\" id=\"marker-name-text\">{this.name}</tspan></text>\n";
svg += "</g>\n";
if(this.icon == "person") {
svg += "<g inkscape:groupmode=\"layer\" id=\"marker-icon\" inkscape:label=\"Icon\" transform=\"translate(42.5 45) scale(0.8)\">\n";
svg += new SVGPerson(this.query, false);
svg += "</g>\n";
}
return svg;
}
}
}

View File

@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Svg {
public class SVGPerson : SVGFile {
private String organisation;
private String function;
private String rang;
private String text;
private String[] typs;
public SVGPerson(String query, Boolean withOutline = true) : base(query, 74.953316, 84.703323, new List<Double>() { -37.5, -12, 74.953316, 84.703323 }, withOutline) {
this.css.Add("#person-layer-org rect {\n stroke: black;\n stroke-width: 3px;\n}");
this.css.Add("#person-layer-funct path {\n stroke: #000000;\n stroke-width: 3px;\n fill: #000000;\n}");
this.css.Add("#person-layer-rang circle {\n fill: #000000;\n}");
this.css.Add("#person-layer-typ tspan {\n font-size: 20px;\n font-family: DIN1451;\n text-align: center;\n text-anchor: middle;\n fill: #000000;\n}");
this.css.Add("#person-layer-typ line {\n stroke-width: 3px;\n stroke: black;\n}");
}
public static String ParseConfig(JsonData json) => "api/svg/person.svg" + DictionaryConfigToString(GenerateConfig(json));
protected override void ParseParams() {
String[] parts = this.query.Split('&');
foreach(String part in parts) {
String[] keyvalue = part.Split('=');
if(keyvalue.Length == 2) {
switch(keyvalue[0].ToLower()) {
case "person-org":
this.organisation = keyvalue[1].ToLower();
break;
case "person-funct":
this.function = keyvalue[1].ToLower();
break;
case "person-rang":
this.rang = keyvalue[1].ToLower();
break;
case "person-text":
this.text = keyvalue[1];
break;
case "person-typ":
this.typs = keyvalue[1].ToLower().Split(",");
break;
}
}
}
}
public static Dictionary<String, String> GenerateConfig(JsonData json) {
Dictionary<String, String> config = new Dictionary<String, String>();
if(json.ContainsKey("org") && json["org"].IsString) {
config.Add("person-org", json["org"].ToString());
}
if(json.ContainsKey("funct") && json["funct"].IsString) {
config.Add("person-funct", json["funct"].ToString());
}
if(json.ContainsKey("rang") && json["rang"].IsString) {
config.Add("person-rang", json["rang"].ToString());
}
if(json.ContainsKey("text") && json["text"].IsString) {
config.Add("person-text", json["text"].ToString());
}
if(json.ContainsKey("typ") && json["typ"].IsArray) {
List<String> typs = new List<String>();
foreach(JsonData item in json["person"]["typ"]) {
if(item.IsString) {
typs.Add(item.ToString());
}
}
config.Add("person-typ", String.Join(",", typs));
}
return config;
}
protected override String DrawSvgContent() {
String svg = "";
if(this.organisation != null && this.orglookup.ContainsKey(this.organisation)) {
svg += "<g inkscape:groupmode=\"layer\" id=\"person-layer-org\" inkscape:label=\"Organisation\">\n";
svg += $"<g inkscape:groupmode=\"layer\" id=\"person-layer-org-{this.organisation}\" inkscape:label=\"{this.orglookup[this.organisation].Name}\">\n";
svg += $"<rect style=\"fill: {this.orglookup[this.organisation].Color}\" height=\"50\" width=\"50\" transform=\"rotate(45)\"/>\n";
svg += "</g>\n";
svg += "</g>\n";
}
if(this.function != null && this.funclookup.ContainsKey(this.function)) {
svg += "<g inkscape:groupmode=\"layer\" id=\"person-layer-funct\" inkscape:label=\"Funktion\">\n";
svg += $"<g inkscape:groupmode=\"layer\" id=\"person-layer-funct-{this.function}\" inkscape:label=\"{this.funclookup[this.function].Name}\">\n";
svg += $"<path d=\"{this.funclookup[this.function].Path}\" />\n";
svg += "</g>\n";
svg += "</g>\n";
}
if(this.rang != null && this.ranglookup.ContainsKey(this.rang)) {
svg += "<g inkscape:groupmode=\"layer\" id=\"person-layer-rang\" inkscape:label=\"Rang\">\n";
svg += $"<g inkscape:groupmode=\"layer\" id=\"person-layer-rang-{this.rang}\" inkscape:label=\"{this.ranglookup[this.rang].Name}\">\n";
svg += "<g style=\"display: inline;\">\n";
foreach(Double item in this.ranglookup[this.rang].Circles) {
svg += $"<circle cx=\"{item.ToString(CultureInfo.InvariantCulture)}\" cy=\"-8\" r=\"3\" />\n";
}
svg += "</g>\n";
svg += "</g>\n";
svg += "</g>\n";
}
if(this.text != null || this.typs != null && this.typs.All(x => this.typlookup.ContainsKey(x))) {
svg += "<g inkscape:groupmode=\"layer\" id=\"person-layer-typ\" inkscape:label=\"Typ\">\n";
if(this.text != null && this.typs == null) {
svg += $"<text><tspan y=\"42\" x=\"0\" id=\"person-layer-typ-text\">{this.text}</tspan></text>\n";
}
if(this.text == null && this.typs != null && this.typs.All(x => this.typlookup.ContainsKey(x))) {
foreach(String typ in this.typs) {
svg += $"<g id=\"person-layer-typ-{typ}\" inkscape:label=\"{this.typlookup[typ].Name}\">\n";
foreach(Tuple<Double, Double, Double, Double> item in this.typlookup[typ].Lines) {
svg += $"<line x1=\"{item.Item1.ToString(CultureInfo.InvariantCulture)}\" y1=\"{item.Item2.ToString(CultureInfo.InvariantCulture)}\" x2=\"{item.Item3.ToString(CultureInfo.InvariantCulture)}\" y2=\"{item.Item4.ToString(CultureInfo.InvariantCulture)}\" />\n";
}
svg += "</g>\n";
}
}
svg += "</g>\n";
}
return svg;
}
private readonly Dictionary<String, Organistaion> orglookup = new Dictionary<String, Organistaion>() {
{ "thw", new Organistaion("THW", "#0069b4") },
{ "fw", new Organistaion("Feuerwehr", "#e30613") },
{ "hilo", new Organistaion("Hilo", "#ffffff") },
{ "pol", new Organistaion("Polizei", "#13a538") },
{ "fueh", new Organistaion("Führung", "#ffed00") },
{ "sonst", new Organistaion("Sonstig", "#ec6725") }
};
private readonly Dictionary<String, Funktion> funclookup = new Dictionary<String, Funktion>() {
{ "sonder", new Funktion("Sonder", "M -10,10 H 10") },
{ "fueh", new Funktion("Führungskraft", "M -10,10 H 10 L 0,0 Z") }
};
private readonly Dictionary<String, Rang> ranglookup = new Dictionary<String, Rang>() {
{ "trupp", new Rang("Truppführer", new List<Double>() { 0 }) },
{ "grupp", new Rang("Gruppenführer", new List<Double>() { -4.5, 4.5 }) },
{ "zug", new Rang("Zugführer", new List<Double>() { -9, 0, 9 }) }
};
private readonly Dictionary<String, Typ> typlookup = new Dictionary<String, Typ>() {
{ "loesch", new Typ("Brandbekämpfung/Löscheinsatz", new List<Tuple<Double, Double, Double, Double>>() { new Tuple<Double, Double, Double, Double>(-35, 35.5, 35, 35.5), new Tuple<Double, Double, Double, Double>(17.5, 35.5, 25, 26), new Tuple<Double, Double, Double, Double>(17.5, 35.5, 25, 44) }) },
{ "sani", new Typ("Rettungswesen, Sanitätswesen, Gesundheitswesen", new List<Tuple<Double, Double, Double, Double>>() { new Tuple<Double, Double, Double, Double>(0, 0, 0, 70), new Tuple<Double, Double, Double, Double>(-35, 35.5, 35, 35.5) }) },
{ "betreu", new Typ("Betreuung", new List<Tuple<Double, Double, Double, Double>>() { new Tuple<Double, Double, Double, Double>(-17, 53, 0, 35.5), new Tuple<Double, Double, Double, Double>(17, 53, 0, 35.5) }) }
};
private struct Organistaion {
public String Name {
get;
}
public String Color {
get;
}
public Organistaion(String name, String color) {
this.Name = name;
this.Color = color;
}
}
private struct Funktion {
public String Name {
get;
}
public String Path {
get;
}
public Funktion(String name, String path) {
this.Name = name;
this.Path = path;
}
}
private struct Rang {
public String Name {
get;
}
public List<Double> Circles {
get;
}
public Rang(String name, List<Double> circles) {
this.Name = name;
this.Circles = circles;
}
}
private struct Typ {
public String Name {
get;
}
public List<Tuple<Double, Double, Double, Double>> Lines {
get;
}
public Typ(String name, List<Tuple<Double, Double, Double, Double>> lines) {
this.Name = name;
this.Lines = lines;
}
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using BlubbFish.Utils;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Svg {
public class SvgModel : OwnSingeton<SvgModel> {
private readonly Dictionary<String, SVGFile> svgtable = new Dictionary<String, SVGFile>();
public Boolean ParseRequest(HttpListenerContext cont) {
Byte[] svg = this.GetSvg(cont.Request.Url.PathAndQuery);
if(svg.Length > 0) {
cont.Response.ContentType = "image/svg+xml";
cont.Response.ContentLength64 = svg.Length;
cont.Response.OutputStream.Write(svg, 0, svg.Length);
Console.WriteLine("200 - " + cont.Request.Url.PathAndQuery);
return true;
}
cont.Response.StatusCode = 404;
Helper.WriteError("404 - " + cont.Request.Url.PathAndQuery + " not found!");
return false;
}
private Byte[] GetSvg(String pathAndQuery) {
if(this.svgtable.ContainsKey(pathAndQuery)) {
return Encoding.UTF8.GetBytes(this.svgtable[pathAndQuery].ToString());
} else {
if(pathAndQuery.StartsWith("/api/svg/marker.svg") && pathAndQuery.Contains("?")) {
String query = pathAndQuery[(pathAndQuery.IndexOf('?') + 1)..];
this.svgtable.Add(pathAndQuery, new SVGMarker(query));
return Encoding.UTF8.GetBytes(this.svgtable[pathAndQuery].ToString());
} else if(pathAndQuery.StartsWith("/api/svg/person.svg") && pathAndQuery.Contains("?")) {
String query = pathAndQuery[(pathAndQuery.IndexOf('?') + 1)..];
this.svgtable.Add(pathAndQuery, new SVGPerson(query));
return Encoding.UTF8.GetBytes(this.svgtable[pathAndQuery].ToString());
}
}
return new Byte[0];
}
}
}

View File

@ -1,141 +1,48 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using BlubbFish.Utils;
using BlubbFish.Utils.IoT.Bots;
using BlubbFish.Utils.IoT.Connector;
using BlubbFish.Utils.IoT.Events;
using Fraunhofer.Fit.IoT.LoraMap.Model;
using Fraunhofer.Fit.IoT.LoraMap.Model.Admin;
using Fraunhofer.Fit.IoT.LoraMap.Model.Camera;
using Fraunhofer.Fit.IoT.LoraMap.Model.Position;
using Fraunhofer.Fit.IoT.LoraMap.Model.Sensor;
using Fraunhofer.Fit.IoT.LoraMap.Model.Svg;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap {
class Server : Webserver {
private readonly SortedDictionary<String, PositionItem> positions = new SortedDictionary<String, PositionItem>();
private readonly SortedDictionary<String, AlarmItem> alarms = new SortedDictionary<String, AlarmItem>();
private readonly SortedDictionary<String, Camera> counter = new SortedDictionary<String, Camera>();
private readonly SortedDictionary<String, Crowd> density = new SortedDictionary<String, Crowd>();
private readonly SortedDictionary<String, Fight> fights = new SortedDictionary<String, Fight>();
private readonly SortedDictionary<String, EnviromentData> sensors = new SortedDictionary<String, EnviromentData>();
private JsonData marker;
private readonly Settings settings;
private readonly WeatherWarnings weather;
private readonly Dictionary<String, Marker> markertable = new Dictionary<String, Marker>();
private readonly SortedDictionary<String, Object> jsonapi = new SortedDictionary<String, Object>() {
{ "camera", CameraModel.Instance },
{ "position", PositionModel.Instance },
{ "sensor", SensorModel.Instance },
{ "settings", Settings.Instance.External },
};
private readonly AdminModel admin;
private readonly Object lockData = new Object();
private readonly Object lockPanic = new Object();
private readonly Object lockFight = new Object();
private readonly Object lockCount = new Object();
private readonly Object lockDensy = new Object();
private readonly Object lockSensor = new Object();
public Server(ADataBackend backend, Dictionary<String, String> settings) : base(backend, settings, null) {
this.logger.SetPath(settings["loggingpath"]);
this.CheckJsonFiles();
this.admin = new AdminModel(settings);
this.marker = JsonMapper.ToObject(File.ReadAllText("json/names.json"));
this.admin.NamesUpdate += this.AdminModelUpdateNames;
this.settings = new Settings();
this.weather = new WeatherWarnings(this.settings);
this.admin.SettingsUpdate += this.settings.AdminModelUpdateSettings;
this.admin.SettingsUpdate += Settings.Instance.ReloadSettings;
this.admin.GeoUpdate += Settings.Instance.ReloadGeo;
this.admin.NamesUpdate += PositionModel.Instance.ReloadNames;
this.StartListen();
this.WaitForShutdown();
this.Dispose();
}
private void AdminModelUpdateNames(Object sender, EventArgs e) {
this.marker = JsonMapper.ToObject(File.ReadAllText("json/names.json"));
foreach(KeyValuePair<String, PositionItem> item in this.positions) {
item.Value.UpdateMarker(this.marker, item.Key);
}
Console.WriteLine("Namen und Icons aktualisiert!");
}
private void CheckJsonFiles() {
if(!Directory.Exists("json")) {
_ = Directory.CreateDirectory("json");
}
if(!File.Exists("json/names.json")) {
File.WriteAllText("json/names.json", "{}");
}
if(!File.Exists("json/geo.json")) {
File.WriteAllText("json/geo.json", "{}");
}
if (!File.Exists("json/settings.json")) {
File.WriteAllText("json/settings.json", "{}");
}
}
protected override void Backend_MessageIncomming(Object sender, BackendEvent mqtt) {
try {
JsonData d = JsonMapper.ToObject(mqtt.Message);
if(((String)mqtt.From).Contains("lora/data") && PositionItem.CheckJson(d)) {
String name = PositionItem.GetId(d);
lock(this.lockData) {
if(this.positions.ContainsKey(name)) {
this.positions[name].Update(d);
} else {
this.positions.Add(name, new PositionItem(d, this.marker));
}
}
Console.WriteLine("Koordinate erhalten!");
} else if(((String)mqtt.From).Contains("lora/panic") && PositionItem.CheckJson(d)) {
String name = PositionItem.GetId(d);
lock(this.lockPanic) {
if(this.alarms.ContainsKey(name)) {
this.alarms[name].Update(d);
} else {
this.alarms.Add(name, new AlarmItem(d));
}
}
lock(this.lockData) {
if(this.positions.ContainsKey(name)) {
this.positions[name].Update(d);
} else {
this.positions.Add(name, new PositionItem(d, this.marker));
}
}
Console.WriteLine("PANIC erhalten!");
} else if(((String)mqtt.From).Contains("camera/count") && Camera.CheckJson(d)) {
String cameraid = Camera.GetId(d);
lock(this.lockCount) {
if(this.counter.ContainsKey(cameraid)) {
this.counter[cameraid].Update(d);
} else {
this.counter.Add(cameraid, new Camera(d));
}
}
} else if((((String)mqtt.From).Contains("sfn/crowd_density_local") || ((String)mqtt.From).Contains("camera/crowd")) && Crowd.CheckJsonCrowdDensityLocal(d) || ((String)mqtt.From).Contains("sfn/flow") && Crowd.CheckJsonFlow(d)) {
String cameraid = Crowd.GetId(d);
lock(this.lockDensy) {
if(this.density.ContainsKey(cameraid)) {
this.density[cameraid].Update(d);
} else {
this.density.Add(cameraid, new Crowd(d));
}
}
} else if((((String)mqtt.From).Contains("camera/fighting_detection") || ((String)mqtt.From).Contains("sfn/fighting_detection")) && Fight.CheckJsonFightingDetection(d)) {
String cameraid = Fight.GetId(d);
lock(this.lockFight) {
if(this.fights.ContainsKey(cameraid)) {
this.fights[cameraid].Update(d);
} else {
this.fights.Add(cameraid, new Fight(d));
}
}
} else if(((String)mqtt.From).Contains("lora/sensor") && EnviromentData.CheckJson(d)) {
String sensorid = EnviromentData.GetId(d);
lock(this.lockSensor) {
if(this.sensors.ContainsKey(sensorid)) {
this.sensors[sensorid].Update(d);
} else {
this.sensors.Add(sensorid, new EnviromentData(d));
}
}
Console.WriteLine("Umweltdaten erhalten!");
}
PositionModel.Instance.ParseMqttJson(d, (String)mqtt.From);
CameraModel.Instance.ParseMqttJson(d, (String)mqtt.From);
SensorModel.Instance.ParseMqttJson(d, (String)mqtt.From);
} catch(Exception e) {
Helper.WriteError("Backend_MessageIncomming(): "+e.Message + "\n\n" + e.StackTrace);
}
@ -143,40 +50,24 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
protected override Boolean SendWebserverResponse(HttpListenerContext cont) {
try {
if (cont.Request.Url.PathAndQuery.StartsWith("/get1000")) {
return SendJsonResponse(new Dictionary<String, Object>() {
{ "loc", this.positions },
{ "panic", this.alarms },
{ "cameracount", this.counter },
{ "crowdcount", this.density },
{ "fightdedect", this.fights },
{ "weatherwarnings", this.weather.Warnungen },
{ "sensors", this.sensors }
}, cont);
} else if (cont.Request.Url.PathAndQuery.StartsWith("/get60000")) {
return SendJsonResponse(new Dictionary<String, Object>() {
{ "currenttime", new Dictionary<String, DateTime>() { { "utc", DateTime.UtcNow } } }
}, cont);
} else if (cont.Request.Url.PathAndQuery.StartsWith("/getonce")) {
return SendJsonResponse(new Dictionary<String, Object>() {
{ "getlayer", this.FindMapLayer() },
{ "getgeo", JsonMapper.ToObject(File.ReadAllText("json/geo.json")) },
{ "startup", this.settings }
}, cont);
} else if (cont.Request.Url.PathAndQuery.StartsWith("/icons/marker/Marker.svg") && cont.Request.Url.PathAndQuery.Contains("?")) {
String hash = cont.Request.Url.PathAndQuery.Substring(cont.Request.Url.PathAndQuery.IndexOf('?') + 1);
if (!this.markertable.ContainsKey(hash)) {
this.markertable.Add(hash, new Marker(hash));
if(cont.Request.Url.AbsolutePath.StartsWith("/api/json/")) {
if(cont.Request.Url.AbsolutePath.Length > 10) {
String parts = cont.Request.Url.AbsolutePath[10..];
Dictionary<String, Object> ret = new Dictionary<String, Object>();
foreach(String part in parts.Split(",")) {
if(this.jsonapi.ContainsKey(part)) {
ret.Add(part, this.jsonapi[part]);
}
}
return SendJsonResponse(ret, cont);
}
cont.Response.ContentType = "image/svg+xml";
Byte[] buf = Encoding.UTF8.GetBytes(this.markertable[hash].ToString());
cont.Response.ContentLength64 = buf.Length;
cont.Response.OutputStream.Write(buf, 0, buf.Length);
Console.WriteLine("200 - " + cont.Request.Url.PathAndQuery);
return true;
} else if (cont.Request.Url.PathAndQuery.StartsWith("/admin")) {
} else if(cont.Request.Url.AbsolutePath.StartsWith("/api/time")) {
return SendJsonResponse(new Dictionary<String, DateTime>() { { "utc", DateTime.UtcNow } }, cont);
} else if(cont.Request.Url.AbsolutePath.StartsWith("/api/svg/")) {
return SvgModel.Instance.ParseRequest(cont);
} else if(cont.Request.Url.PathAndQuery.StartsWith("/admin/")) {
return this.admin.ParseReuqest(cont);
} else if (cont.Request.Url.PathAndQuery.StartsWith("/maps/")) {
} else if(cont.Request.Url.PathAndQuery.StartsWith("/maps/")) {
return SendFileResponse(cont, "resources", false);
}
} catch(Exception e) {
@ -187,43 +78,8 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
return SendFileResponse(cont);
}
private Dictionary<String, Dictionary<String, Object>> FindMapLayer() {
Dictionary<String, Dictionary<String, Object>> ret = new Dictionary<String, Dictionary<String, Object>> {
{ "online", new Dictionary<String, Object>() {
{ "title", "Online Map" },
{ "url", "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" },
{ "attribution", "&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors" },
{ "minZoom", 1 },
{ "maxZoom", 19 }
} }
};
if(Directory.Exists("resources" + Path.DirectorySeparatorChar + "maps")) {
String[] dirs = Directory.GetDirectories("resources" + Path.DirectorySeparatorChar + "maps");
foreach(String dir in dirs) {
if(File.Exists(dir + Path.DirectorySeparatorChar + "tiles.json")) {
try {
JsonData map = JsonMapper.ToObject(File.ReadAllText(dir + Path.DirectorySeparatorChar + "tiles.json"));
Dictionary<String, Object> entry = new Dictionary<String, Object> {
{ "title", (String)map["name"] },
{ "url", (String)map["tiles"][0] },
{ "attribution", (String)map["attribution"] },
{ "minZoom", (Int32)map["minzoom"] },
{ "maxZoom", (Int32)map["maxzoom"] },
{ "bounds", new Dictionary<String, Object>() {
{ "corner1", new Double[] { (Double)map["bounds"][0], (Double)map["bounds"][1] } },
{ "corner2", new Double[] { (Double)map["bounds"][2], (Double)map["bounds"][3] } }
} }
};
ret.Add(dir.Substring(("resources" + Path.DirectorySeparatorChar + "maps").Length+1), entry);
} catch { }
}
}
}
return ret;
}
public override void Dispose() {
this.weather.Dispose();
SensorModel.Instance.Dispose();
base.Dispose();
}
}

View File

@ -2,7 +2,7 @@
HOMEDIR=$HOME
ROOT="$HOMEDIR/deb"
OUTPUT="../bin/Release/netcoreapp3.0"
OUTPUT="../bin/Release/netcoreapp3.1"
DEBNAME="loramap"
CSPROJFILE="Lora-Map.csproj"

View File

@ -5,7 +5,6 @@
<meta charset="utf-8" />
<title>Adminpannel Lora-Map</title>
<link rel="stylesheet" type="text/css" href="css/global.css" />
<script type="text/javascript" src="js/menu.js"></script>
</head>
<body>
<div id="header">Adminpannel Lora-Map</div>
@ -18,5 +17,9 @@
</ul>
</div>
<div id="content">Wilkommen im Adminbereich</div>
<script type="text/javascript" src="js/adminmenu.js"></script>
<script type="text/javascript" src="js/eximport.js"></script>
<script type="text/javascript" src="js/nameseditor.js"></script>
<script type="text/javascript" src="js/settings.js"></script>
</body>
</html>

View File

@ -0,0 +1,42 @@
var AdminMenu = {
//public functions
ExImport: function () {
var ajaxrequest = new XMLHttpRequest();
ajaxrequest.onreadystatechange = function () {
if (ajaxrequest.readyState === 4 && ajaxrequest.status === 200) {
var json = JSON.parse(ajaxrequest.responseText);
ExImport.ParseJson(json.name, json.geo, json.setting);
}
};
ajaxrequest.open("GET", "/admin/api/json/name,geo,setting", true);
ajaxrequest.send();
return false;
},
Names: function () {
var ajaxrequest = new XMLHttpRequest();
ajaxrequest.onreadystatechange = function () {
if (ajaxrequest.readyState === 4 && ajaxrequest.status === 200) {
var json = JSON.parse(ajaxrequest.responseText);
NamesEditor.ParseJson(json.name);
}
};
ajaxrequest.open("GET", "/admin/api/json/name", true);
ajaxrequest.send();
return false;
},
Overlay: function () {
return false;
},
Settings: function () {
var ajaxrequest = new XMLHttpRequest();
ajaxrequest.onreadystatechange = function () {
if (ajaxrequest.readyState === 4 && ajaxrequest.status === 200) {
var json = JSON.parse(ajaxrequest.responseText);
Settings.ParseJson(json.setting);
}
};
ajaxrequest.open("GET", "/admin/api/json/setting", true);
ajaxrequest.send();
return false;
}
};

View File

@ -0,0 +1,56 @@
var ExImport = {
//public functions
ParseJson: function (jsonnames, jsongeo, jsonsettings) {
html = "<div id='eximport'><div class='title'>Ex- und Import der Einstellungen</div>";
html += "<div class='names'>names.json (Namen und Icons)<br/><textarea id='ex_names'></textarea> <img src='../icons/general/save.png' onclick='ExImport.SaveNames()' class='pointer'></div>";
html += "<div class='names'>geo.json (Layer on the MAP) <a href='https://mapbox.github.io/togeojson/'>Kml Konverter</a><br/><textarea id='ex_geo'></textarea> <img src='../icons/general/save.png' onclick='ExImport.SaveGeo()' class='pointer'></div>";
html += "<div class='names'>settings.json (Settings of the Map)<br/><textarea id='ex_settings'></textarea> <img src='../icons/general/save.png' onclick='ExImport.SaveSettings()' class='pointer'></div>";
html += "</div>";
document.getElementById("content").innerHTML = html;
document.getElementById("ex_names").value = JSON.stringify(jsonnames);
document.getElementById("ex_geo").value = JSON.stringify(jsongeo);
document.getElementById("ex_settings").value = JSON.stringify(jsonsettings);
},
SaveNames: function () {
var savenames = new XMLHttpRequest();
savenames.onreadystatechange = function () {
if (savenames.readyState === 4) {
if (savenames.status === 200) {
alert("Änderungen an names.json gespeichert!");
} else if (savenames.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savenames.open("PUT", "/admin/api/json/name", true);
savenames.send(document.getElementById("ex_names").value);
},
SaveGeo: function () {
var savegeo = new XMLHttpRequest();
savegeo.onreadystatechange = function () {
if (savegeo.readyState === 4) {
if (savegeo.status === 200) {
alert("Änderungen an geo.json gespeichert!");
} else if (savegeo.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savegeo.open("PUT", "/admin/api/json/geo", true);
savegeo.send(document.getElementById("ex_geo").value);
},
SaveSettings: function () {
var savesettings = new XMLHttpRequest();
savesettings.onreadystatechange = function () {
if (savesettings.readyState === 4) {
if (savesettings.status === 200) {
alert("Änderungen an settings.json gespeichert!");
} else if (savesettings.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savesettings.open("PUT", "/admin/api/json/setting", true);
savesettings.send(document.getElementById("ex_settings").value);
}
};

View File

@ -1,741 +0,0 @@
var AdminMenu = {
Names: function () {
var ajaxnames = new XMLHttpRequest();
ajaxnames.onreadystatechange = function () {
if (ajaxnames.readyState === 4 && ajaxnames.status === 200) {
NamesEditor.ParseJson(ajaxnames.responseText);
}
};
ajaxnames.open("GET", "/admin/get_json_names", true);
ajaxnames.send();
return false;
},
Overlay: function () {
return false;
},
Settings: function () {
var ajaxsettings = new XMLHttpRequest();
ajaxsettings.onreadystatechange = function () {
if (ajaxsettings.readyState === 4 && ajaxsettings.status === 200) {
Settings.ParseJson(JSON.parse(ajaxsettings.responseText));
}
};
ajaxsettings.open("GET", "/admin/get_json_settings", true);
ajaxsettings.send();
return false;
},
ExImport: function () {
var ajaxnames = new XMLHttpRequest();
ajaxnames.onreadystatechange = function () {
if (ajaxnames.readyState === 4 && ajaxnames.status === 200) {
var ajaxgeo = new XMLHttpRequest();
ajaxgeo.onreadystatechange = function () {
if (ajaxgeo.readyState === 4 && ajaxgeo.status === 200) {
var ajaxsettings = new XMLHttpRequest();
ajaxsettings.onreadystatechange = function () {
if (ajaxsettings.readyState === 4 && ajaxsettings.status === 200) {
ExImport.ParseJson(ajaxnames.responseText, ajaxgeo.responseText, ajaxsettings.responseText);
}
};
ajaxsettings.open("GET", "/admin/get_json_settings", true);
ajaxsettings.send();
}
};
ajaxgeo.open("GET", "/admin/get_json_geo", true);
ajaxgeo.send();
}
};
ajaxnames.open("GET", "/admin/get_json_names", true);
ajaxnames.send();
return false;
}
};
var NamesEditor = {
iconeditorcounter: 0,
filterGropus: { no: "immer Sichtbar", fw: "Feuerwehr", sani: "Sanitäter", pol: "Polizei", oa: "Ordnungsamt", si: "Sicherheitsdienst", thw: "Technisches Hilfswerk", crew: "Veranstalter", dev: "Entwickler" },
ParseJson: function (jsontext) {
document.getElementById("content").innerHTML = "";
var namesconfig = JSON.parse(jsontext);
var html = "<div id='nameeditor'><div class='title'>Namenseinträge in den Einstellungen</div>";
html += "<table id='nametable' class='settingstable'>";
html += "<thead><tr><th width='60'>ID</th><th width='250'>Name</th><th width='65'>Icon</th><th width='150'>Filter Gruppe</th><th width='50'></th></tr></thead>";
html += "<tbody>";
for (var id in namesconfig) {
if (namesconfig.hasOwnProperty(id)) {
var nameentry = namesconfig[id];
html += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + nameentry["name"] + "</td>";
if (nameentry.hasOwnProperty("marker.svg")) {
html += "<td>" + this.ParseIcon(nameentry["marker.svg"]) + "</td>";
} else if (nameentry.hasOwnProperty("icon")) {
html += "<td><img src='"+nameentry["icon"]+"'></td>";
} else {
html += "<td><img src='../js/leaflet/images/marker-icon.png'></td>";
}
var gfilter = typeof nameentry.Group === "undefined" ? "no" : nameentry.Group;
html += "<td rel='" + gfilter + "'>" + this.filterGropus[gfilter] + "</td>";
html += "<td><img src='../icons/general/edit.png' onclick='NamesEditor.Edit(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
}
html += "</tbody>";
html += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='NamesEditor.Add()' class='pointer'> <img src='../icons/general/save.png' onclick='NamesEditor.Save()' class='pointer'></td></tr></tfoot>";
html += "</table>";
document.getElementById("content").innerHTML = html + "</div>";
},
ParseIcon: function (markerobj) {
var url = "../icons/marker/Marker.svg";
if (markerobj.hasOwnProperty("person")) {
url += "?icon=person&marker-bg=hidden";
if (markerobj["person"].hasOwnProperty("org")) {
url += "&person-org=" + markerobj["person"]["org"];
}
if(markerobj["person"].hasOwnProperty("funct")) {
url += "&person-funct=" + markerobj["person"]["funct"];
}
if(markerobj["person"].hasOwnProperty("rang")) {
url += "&person-rang=" + markerobj["person"]["rang"];
}
if(markerobj["person"].hasOwnProperty("text")) {
url += "&person-text=" + markerobj["person"]["text"];
}
if (markerobj["person"].hasOwnProperty("typ") && Array.isArray(markerobj["person"]["typ"])) {
for (i in markerobj["person"]["typ"]) {
url += "&person-typ=" + markerobj["person"]["typ"][i];
}
}
}
return "<object data='"+url+"' type='image/svg+xml' style='height:50px; width:50px;'></object>";
},
BuildIconJson: function (url) {
var query = this.SplitQueryIntoObject(this.SplitUrlIntoParts(url).query);
var markerobj = {};
if (query.hasOwnProperty("icon") && query["icon"] === "person") {
markerobj["person"] = {};
if (query.hasOwnProperty("person-org")) {
markerobj["person"]["org"] = query["person-org"];
}
if (query.hasOwnProperty("person-funct")) {
markerobj["person"]["funct"] = query["person-funct"];
}
if (query.hasOwnProperty("person-rang")) {
markerobj["person"]["rang"] = query["person-rang"];
}
if (query.hasOwnProperty("person-text")) {
markerobj["person"]["text"] = query["person-text"];
}
if (query.hasOwnProperty("person-typ")) {
if (Array.isArray(query["person-typ"])) {
markerobj["person"]["typ"] = new Array();
for (var i in query["person-typ"]) {
markerobj["person"]["typ"].push(query["person-typ"][i]);
}
} else {
markerobj["person"]["typ"] = new Array();
markerobj["person"]["typ"].push(query["person-typ"]);
}
}
}
return markerobj;
},
Add: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 55px;'/></td>";
newrow.innerHTML += "<td><input style='width: 245px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> wähle Icon</td>";
newrow.innerHTML += "<td>" + this.CreateSelectBox("", "item", { item: "" }, this.filterGropus, null, null, true);
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='NamesEditor.SaveRow(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("nametable").children[1].appendChild(newrow);
},
Save: function () {
var rows = document.getElementById("nametable").children[1].children;
var namejson = {};
for (var i = 0; i < rows.length; i++) {
if (rows[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
var id = rows[i].children[0].innerText;
var name = rows[i].children[1].innerText;
namejson[id] = { "name": name, "Group": rows[i].children[3].attributes.rel.nodeValue };
if (rows[i].children[2].children[0].hasAttribute("data")) {
namejson[id]["marker.svg"] = this.BuildIconJson(rows[i].children[2].children[0].data);
}
}
var savenames = new XMLHttpRequest();
savenames.onreadystatechange = function () {
if (savenames.readyState === 4) {
if (savenames.status === 200) {
alert("Änderungen gespeichert!");
} else if (savenames.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savenames.open("POST", "/admin/set_json_names", true);
savenames.send(JSON.stringify(namejson));
},
Delete: function (el) {
var name = el.firstChild.innerHTML;
var answ = window.prompt("Wollen sie den Eintrag für \"" + name + "\" wirklich löschen?", "");
if (answ !== null) {
el.parentNode.removeChild(el);
}
},
Edit: function (el) {
var id = el.children[0].innerText;
var name = el.children[1].innerText;
var url = null;
var gfilter = el.children[3].attributes.rel.nodeValue;
if (el.children[2].children[0].hasAttribute("data")) {
url = el.children[2].children[0].data;
}
el.innerHTML = "<td><input style='width: 55px;' value='" + id + "'/></td>";
el.innerHTML += "<td><input style='width: 245px;' value='" + name + "'/></td>";
if (url === null) {
el.innerHTML += "<td><img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> wähle Icon</td>";
} else {
el.innerHTML += "<td><img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> <object data='" + url + "' type='image/svg+xml' style='height:50px; width:50px;'></object></td>";
}
el.innerHTML += "<td>" + this.CreateSelectBox("", "item", { item: gfilter }, this.filterGropus, null, null, true);
el.innerHTML += "<td><img src='../icons/general/save.png' onclick='NamesEditor.SaveRow(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
Abort: function (el) {
el.parentNode.removeChild(el);
},
SaveRow: function (el) {
var id = el.children[0].children[0].value;
var name = el.children[1].children[0].value;
var url = null;
var gfilter = el.children[3].children[0].selectedOptions[0].value;
if (gfilter === "---") {
gfilter = "no";
}
if (el.children[2].children.length === 2) {
url = el.children[2].children[1].data;
}
el.innerHTML = "<td>" + id + "</td>" +
"<td>" + name + "</td>";
if (url === null) {
el.innerHTML += "<td><img src='../js/leaflet/images/marker-icon.png'></td>";
} else {
el.innerHTML += "<td><object data='" + url +"' type='image/svg+xml' style='height:50px; width:50px;'></object></td>";
}
el.innerHTML += "<td rel='" + gfilter + "'>" + this.filterGropus[gfilter] + "</td>";
el.innerHTML += "<td><img src='../icons/general/edit.png' onclick='NamesEditor.Edit(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
IconEditor: function (el) {
var url = "../icons/marker/Marker.svg?marker-bg=hidden";
el.id = "icon_edit_" + this.iconeditorcounter++;
if (el.children.length === 2) {
url = el.children[1].data;
}
var query = this.SplitQueryIntoObject(this.SplitUrlIntoParts(url).query);
var ie = document.createElement("div");
ie.id = "iconeditor";
ie.innerHTML = "<div class='innerbox'>" +
"<div class='preview'><object id='markerprev' data='" + url + "' type='image/svg+xml' style='height:200px; width:200px;'></object></div>" +
"<div class='controls'>" +
this.CreateSelectBox("Typ", "icon", query, { "person": "Person" }, null, "iconeditor-type-") + "<br>" +
"<div id='iconeditor-type-person' style='display: " + (query.hasOwnProperty("icon") && query["icon"] === "person" ? "block" : "none") + ";'>" +
this.CreateSelectBox("Organisation", "person-org", query, { "fw": "Feuerwehr", "thw": "Technisches Hilfswerk", "hilo": "Hilfsorganisationen, Bundeswehr", "fueh": "Einrichtungen der Führung", "pol": "Polizei, Bundespolizei, Zoll", "sonst": "Sonstige Einrichtungen der Gefahrenabwehr" }) + "<br>" +
this.CreateSelectBox("Funktion", "person-funct", query, { "sonder": "Sonder", "fueh": "Führung" }) + "<br>" +
this.CreateSelectBox("Rang", "person-rang", query, { "trupp": "Trupp", "grupp": "Gruppe", "zug":"Zug" }) + "<br>" +
"Text: <input onchange='NamesEditor.ChangeLinkPreview(\"person-text\",this.value);' value='" + (query.hasOwnProperty("person-text") ? query["person-text"] : "") + "'><br>" +
this.CreateSelectBox("Typ", "person-typ", query, { "loesch": "Brandbekämpfung/Löscheinsatz", "sani": "Rettungswesen, Sanitätswesen, Gesundheitswesen", "betreu": "Betreuung" }, true) + "<br>" +
"</div>" +
"</div>" +
"<div class='save'><button onclick='NamesEditor.SaveIconEditor(\"" + el.id + "\"); '>Schließen</botton></div>" +
"</div>";
document.getElementsByTagName("body")[0].appendChild(ie);
},
CreateSelectBox: function (title, key, query, options, muliple, group, noonchange) {
var html = title !== "" ? title + ": " : "";
var onchange = "";
if (!(typeof noonchange !== "undefined" && noonchange === true)) {
var eventtext = "NamesEditor.ChangeLinkPreview(\"" + key + "\",this.selectedOptions);";
if (typeof group !== "undefined" && group !== null) {
eventtext += " document.getElementById(\"" + group + "\"+this.value).style.display = \"block\";'";
}
onchange = " onchange='" + eventtext + "'";
}
html += "<select" + onchange + (typeof muliple !== "undefined" && muliple !== null ? " multiple" : "") + ">";
if (typeof muliple === "undefined" || muliple === null) {
html += "<option>---</option>";
}
for (var value in options) {
if (query.hasOwnProperty(key) && query[key] === value) {
html += "<option value='" + value + "' selected>" + options[value] + "</option>";
} else if (query.hasOwnProperty(key) && Array.isArray(query[key])) {
var notinqueryarray = true;
for (var i in query[key]) {
if (query[key][i] === value) {
notinqueryarray = false;
html += "<option value='" + value + "' selected>" + options[value] + "</option>";
}
}
if (notinqueryarray) {
html += "<option value='" + value + "'>" + options[value] + "</option>";
}
} else {
html += "<option value='" + value + "'>" + options[value] + "</option>";
}
}
html += "</select>";
return html;
},
SaveIconEditor: function (id) {
var cell = document.getElementById(id);
cell.innerHTML = "<img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> <object data='" + document.getElementById("markerprev").data + "' type='image/svg+xml' style='height:50px; width:50px;'></object>";
cell.removeAttribute("id");
document.getElementById("iconeditor").remove();
},
SplitQueryIntoObject: function (query) {
if (query.indexOf("?") !== -1) {
query = query.split("?")[1];
}
var queryobj = {};
var pairs = query.split("&");
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split("=");
if (queryobj.hasOwnProperty(decodeURIComponent(pair[0]))) {
if (Array.isArray(queryobj[decodeURIComponent(pair[0])])) {
queryobj[decodeURIComponent(pair[0])].push(decodeURIComponent(pair[1] || ""));
} else {
var tmp = queryobj[decodeURIComponent(pair[0])];
queryobj[decodeURIComponent(pair[0])] = new Array();
queryobj[decodeURIComponent(pair[0])].push(tmp);
queryobj[decodeURIComponent(pair[0])].push(decodeURIComponent(pair[1] || ""));
}
} else {
queryobj[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || "");
}
}
return queryobj;
},
JoinObjectIntoQuery: function (queryobj) {
var query = new Array();
for (var id in queryobj) {
if (Array.isArray(queryobj[id])) {
for (var i in queryobj[id]) {
query.push(encodeURIComponent(id) + "=" + encodeURIComponent(queryobj[id][i]));
}
} else {
query.push(encodeURIComponent(id) + "=" + encodeURIComponent(queryobj[id]));
}
}
return query.join("&");
},
SplitUrlIntoParts: function (url) {
var parts = url.split("?");
return { "file": parts[0], "query": parts[1] || "" };
},
ChangeLinkPreview: function (key, val) {
var cur = this.SplitUrlIntoParts(document.getElementById("markerprev").data);
var query = this.SplitQueryIntoObject(cur.query);
if (typeof val === "object") {
query[key] = new Array();
for (var i = 0; i < val.length; i++) {
query[key].push(val[i].value);
if (val[i].value === "---") {
delete query[key];
break;
}
}
} else {
if (val === "---" || val === "") {
delete query[key];
} else {
query[key] = val;
}
}
document.getElementById("markerprev").data = cur.file + "?" + this.JoinObjectIntoQuery(query);
}
};
var Settings = {
ParseJson: function (jsonsettings) {
if (typeof jsonsettings.StartPos === "undefined") {
jsonsettings.StartPos = { lat: 0, lon: 0 };
}
if (typeof jsonsettings.CellIds === "undefined") {
jsonsettings.CellIds = [];
}
if (typeof jsonsettings.GridRadius === "undefined") {
jsonsettings.GridRadius = 1000;
}
if (typeof jsonsettings.FightDedection === "undefined") {
jsonsettings.FightDedection = [];
}
if (typeof jsonsettings.CrwodDensity === "undefined") {
jsonsettings.CrwodDensity = [];
}
if (typeof jsonsettings.Counting === "undefined") {
jsonsettings.Counting = [];
}
if (typeof jsonsettings.Sensors === "undefined") {
jsonsettings.Sensors = [];
}
var html = "<div id='settingseditor'><div class='title'>Einstellungen</div>";
html += "<div class='startloc'>Startpunkt: <input value='" + jsonsettings.StartPos.lat + "' id='startlat'> Lat, <input value='" + jsonsettings.StartPos.lon + "' id='startlon'> Lon</div>";
html += "<div class='wetterwarnings'>CellId's für DWD-Wetterwarnungen: <input value='" + jsonsettings.CellIds.join(";") + "' id='wetterids'> (Trennen durch \";\", <a href='https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.html'>cap_warncellids_csv</a>)</div>";
html += "<div class='gridradius'>Radius für das Grid um den Startpunkt: <input value='" + jsonsettings.GridRadius + "' id='gridrad'>m</div>";
html += "<div class='fightdedection'>Fight Dedection Kameras: <br>" + this._renderFightDedection(jsonsettings.FightDedection) + "</div>";
html += "<div class='crowddensity'>Crowd Density Kameras: <br>" + this._renderCrowdDensity(jsonsettings.CrwodDensity) + "</div>";
html += "<div class='sensorsettings'>Sensors: <br>" + this._renderSensorSettings(jsonsettings.Sensors) + "</div>";
html += "<div class='savesettings'><img src='../icons/general/save.png' onclick='Settings.Save()' class='pointer'></div>";
document.getElementById("content").innerHTML = html + "</div>";
},
Save: function () {
var ret = {};
ret.StartPos = {};
ret.StartPos.lat = parseFloat(document.getElementById("startlat").value.replace(",", "."));
ret.StartPos.lon = parseFloat(document.getElementById("startlon").value.replace(",", "."));
ret.CellIds = document.getElementById("wetterids").value.split(";");
ret.GridRadius = parseInt(document.getElementById("gridrad").value);
var rowsf = document.getElementById("fighttable").children[1].children;
var fightjson = {};
for (var i = 0; i < rowsf.length; i++) {
if (rowsf[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
var id = rowsf[i].children[0].innerText;
var coords = rowsf[i].children[1].innerHTML.split("<br>");
var polyjson = [];
for (var j = 0; j < coords.length; j++) {
var coord = coords[j].split(";");
polyjson[j] = { "Lat": this._filterFloat(coord[0]), "Lon": this._filterFloat(coord[1]) };
}
fightjson[id] = { "Poly": polyjson, "Alias": rowsf[i].children[2].innerText, "Level": this._filterFloat(rowsf[i].children[3].innerText) };
}
ret.FightDedection = fightjson;
var rowsc = document.getElementById("crowdtable").children[1].children;
var crowdjson = {};
for (i = 0; i < rowsc.length; i++) {
if (rowsc[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
id = rowsc[i].children[0].innerText;
var num = this._filterFloat(rowsc[i].children[1].innerText);
coords = rowsc[i].children[2].innerHTML.split("<br>");
polyjson = [];
for (j = 0; j < coords.length; j++) {
coord = coords[j].split(";");
polyjson[j] = { "Lat": this._filterFloat(coord[0]), "Lon": this._filterFloat(coord[1]) };
}
crowdjson[id] = {
"Poly": polyjson,
"Count": num,
"Alias": rowsc[i].children[3].innerText
};
}
ret.CrwodDensity = crowdjson;
var rowss = document.getElementById("sensortable").children[1].children;
var sensorjson = {};
for (i = 0; i < rowss.length; i++) {
if (rowss[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
id = rowss[i].children[0].innerText;
coord = rowss[i].children[2].innerHTML.split(";");
sensorjson[id] = {
"Poly": {
"Lat": this._filterFloat(coord[0]),
"Lon": this._filterFloat(coord[1])
},
"Level": this._filterFloat(rowss[i].children[3].innerText),
"Alias": rowss[i].children[1].innerText
};
}
ret.Sensors = sensorjson;
var savesettings = new XMLHttpRequest();
savesettings.onreadystatechange = function () {
if (savesettings.readyState === 4) {
if (savesettings.status === 200) {
alert("Änderungen gespeichert!");
} else if (savesettings.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savesettings.open("POST", "/admin/set_json_settings", true);
savesettings.send(JSON.stringify(ret));
},
_renderSensorSettings: function (json) {
var ret = "";
ret += "<table id='sensortable' class='settingstable'>";
ret += "<thead><tr><th width='150'>ID</th><th width='150'>Alias</th><th width='250'>Koordinaten</th><th width='150'>Warn above</th><th width='50'></th></tr></thead>";
ret += "<tbody>";
for (var id in json) {
ret += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + json[id].Alias + "</td>" +
"<td>" + json[id].Poly.Lat + ";" + json[id].Poly.Lon + "</td>" +
"<td>" + json[id].Level + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
ret += "</tbody>";
ret += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='Settings.AddSensor()' class='pointer'></td></tr></tfoot>";
ret += "</table>";
return ret;
},
_renderFightDedection: function (json) {
var ret = "";
ret += "<table id='fighttable' class='settingstable'>";
ret += "<thead><tr><th width='150'>ID</th><th width='250'>Koordinaten</th><th width='150'>Alias</th><th width='150'>Alertlimit</th><th width='50'></th></tr></thead>";
ret += "<tbody>";
for (var id in json) {
var coords = [];
for (var i = 0; i < json[id].Poly.length; i++) {
coords[i] = json[id].Poly[i].Lat + ";" + json[id].Poly[i].Lon;
}
ret += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + coords.join("<br>") + "</td>" +
"<td>" + json[id].Alias + "</td>" +
"<td>" + json[id].Level + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditFight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
ret += "</tbody>";
ret += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='Settings.AddFight()' class='pointer'></td></tr></tfoot>";
ret += "</table>";
return ret;
},
_renderCrowdDensity: function (json) {
var ret = "";
ret += "<table id='crowdtable' class='settingstable'>";
ret += "<thead><tr><th width='150'>ID</th><th width='200'>Personenanzahl</th><th width='250'>Koordinaten</th><th width='150'>Alias</th><th width='50'></th></tr></thead>";
ret += "<tbody>";
for (var id in json) {
var coords = [];
for (var i = 0; i < json[id].Poly.length; i++) {
coords[i] = json[id].Poly[i].Lat + ";" + json[id].Poly[i].Lon;
}
ret += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + json[id].Count + "</td>" +
"<td>" + coords.join("<br>") + "</td>" +
"<td>" + json[id].Alias + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditDensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
ret += "</tbody>";
ret += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='Settings.AddDensity()' class='pointer'></td></tr></tfoot>";
ret += "</table>";
return ret;
},
AddSensor: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 250px;'/></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='Settings.SaveRowSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("sensortable").children[1].appendChild(newrow);
},
AddFight: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><textarea style='width: 240px;height: 60px;'></textarea></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='Settings.SaveRowfight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("fighttable").children[1].appendChild(newrow);
},
AddDensity: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 195px;'/></td>";
newrow.innerHTML += "<td><textarea style='width: 240px;height: 60px;'></textarea></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='Settings.SaveRowdensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("crowdtable").children[1].appendChild(newrow);
},
Abort: function (el) {
el.parentNode.removeChild(el);
},
SaveRowSensor: function (el) {
var coords = el.children[2].children[0].value;
var coord = coords.split(";");
var fail = false;
if (coord.length !== 2) {
fail = true;
} else if (isNaN(this._filterFloat(coord[0])) || isNaN(this._filterFloat(coord[1]))) {
fail = true;
}
if (isNaN(this._filterFloat(el.children[3].children[0].value))) {
alert("Die Eingabe des Alertlevel erwartet einen Float");
return;
}
if (fail) {
alert("Die Eingabe der Koordinaten ist nicht Korrekt!\n\nBeispiel:\n50.7;7.8");
return;
}
el.innerHTML = "<td>" + el.children[0].children[0].value + "</td>" +
"<td>" + el.children[1].children[0].value + "</td>" +
"<td>" + coords + "</td>" +
"<td>" + this._filterFloat(el.children[3].children[0].value) + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
SaveRowfight: function (el) {
var coords = el.children[1].children[0].value.replace(/\n/gi, "<br>");
var coordscheck = coords.split("<br>");
var fail = false;
for (var i = 0; i < coordscheck.length; i++) {
var coord = coordscheck[i].split(";");
if (coord.length !== 2) {
fail = true;
break;
}
if (isNaN(this._filterFloat(coord[0])) || isNaN(this._filterFloat(coord[1]))) {
fail = true;
break;
}
}
if (isNaN(this._filterFloat(el.children[3].children[0].value))) {
alert("Die Eingabe des Alertlevel erwartet einen Float");
return;
}
if (fail) {
alert("Die Eingabe der Koordinaten ist nicht Korrekt!\n\nBeispiel:\n50.7;7.8\n50.6;7.9");
return;
}
el.innerHTML = "<td>" + el.children[0].children[0].value + "</td>" +
"<td>" + coords + "</td>" +
"<td>" + el.children[2].children[0].value + "</td>" +
"<td>" + this._filterFloat(el.children[3].children[0].value) + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditFight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
SaveRowdensity: function (el) {
var coords = el.children[2].children[0].value.replace(/\n/gi, "<br>");
var coordscheck = coords.split("<br>");
var fail = false;
for (var i = 0; i < coordscheck.length; i++) {
var coord = coordscheck[i].split(";");
if (coord.length !== 2) {
fail = true;
break;
}
if (isNaN(this._filterFloat(coord[0])) || isNaN(this._filterFloat(coord[1]))) {
fail = true;
break;
}
}
if (fail) {
alert("Die Eingabe der Koordinaten ist nicht Korrekt!\n\nBeispiel:\n50.7;7.8\n50.6;7.9");
return;
}
if (isNaN(this._filterFloat(el.children[1].children[0].value))) {
alert("Die Eingabe der Maximalen Anzahl der Personen ist nicht Korrekt, erwarte eine Zahl.");
return;
}
el.innerHTML = "<td>" + el.children[0].children[0].value + "</td>" +
"<td>" + el.children[1].children[0].value + "</td>" +
"<td>" + coords + "</td>" +
"<td>" + el.children[3].children[0].value + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditDensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
Delete: function (el) {
var answ = window.prompt("Wollen sie den Eintrag für \"" + el.firstChild.innerHTML + "\" wirklich löschen?", "");
if (answ !== null) {
el.parentNode.removeChild(el);
}
},
EditSensor: function (el) {
el.innerHTML = "<td><input style='width: 145px;' value='" + el.children[0].innerText + "'/></td>" +
"<td><input style='width: 145px;' value='" + el.children[1].innerText + "'/></td>" +
"<td><input style='width: 250px;' value='" + el.children[2].innerText + "'/></td>" +
"<td><input style='width: 145px;' value='" + el.children[3].innerText + "'/></td>" +
"<td><img src='../icons/general/save.png' onclick='Settings.SaveRowSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
EditFight: function (el) {
el.innerHTML = "<td><input style='width: 145px;' value='" + el.children[0].innerText + "'/></td>" +
"<td><textarea style='width: 240px;height: 60px;'>" + el.children[1].innerText + "</textarea></td>" +
"<td><input style='width: 145px;' value='" + el.children[2].innerText + "'/></td>" +
"<td><input style='width: 145px;' value='" + el.children[3].innerText + "'/></td>" +
"<td><img src='../icons/general/save.png' onclick='Settings.SaveRowfight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
EditDensity: function (el) {
el.innerHTML = "<td><input style='width: 145px;' value='" + el.children[0].innerText + "'/></td>" +
"<td><input style='width: 195px;' value='" + el.children[1].innerText + "'/></td>" +
"<td><textarea style='width: 240px;height: 60px;'>" + el.children[2].innerText + "</textarea></td>" +
"<td><input style='width: 145px;' value='" + el.children[3].innerText + "'/></td>" +
"<td><img src='../icons/general/save.png' onclick='Settings.SaveRowdensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
_filterFloat: function (value) {
if (/^(\-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) {
return Number(value);
}
return NaN;
}
};
var ExImport = {
ParseJson: function (jsonnames, jsongeo, jsonsettings) {
html = "<div id='eximport'><div class='title'>Ex- und Import der Einstellungen</div>";
html += "<div class='names'>names.json (Namen und Icons)<br/><textarea id='ex_names'></textarea> <img src='../icons/general/save.png' onclick='ExImport.SaveNames()' class='pointer'></div>";
html += "<div class='names'>geo.json (Layer on the MAP) <a href='https://mapbox.github.io/togeojson/'>Kml Konverter</a><br/><textarea id='ex_geo'></textarea> <img src='../icons/general/save.png' onclick='ExImport.SaveGeo()' class='pointer'></div>";
html += "<div class='names'>settings.json (Settings of the Map)<br/><textarea id='ex_settings'></textarea> <img src='../icons/general/save.png' onclick='ExImport.SaveSettings()' class='pointer'></div>";
html += "</div>";
document.getElementById("content").innerHTML = html;
document.getElementById("ex_names").value = jsonnames;
document.getElementById("ex_geo").value = jsongeo;
document.getElementById("ex_settings").value = jsonsettings;
},
SaveNames: function () {
var savenames = new XMLHttpRequest();
savenames.onreadystatechange = function () {
if (savenames.readyState === 4) {
if (savenames.status === 200) {
alert("Änderungen an names.json gespeichert!");
} else if (savenames.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savenames.open("POST", "/admin/set_json_names", true);
savenames.send(document.getElementById("ex_names").value);
},
SaveGeo: function () {
var savegeo = new XMLHttpRequest();
savegeo.onreadystatechange = function () {
if (savegeo.readyState === 4) {
if (savegeo.status === 200) {
alert("Änderungen an geo.json gespeichert!");
} else if (savegeo.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savegeo.open("POST", "/admin/set_json_geo", true);
savegeo.send(document.getElementById("ex_geo").value);
},
SaveSettings: function () {
var savesettings = new XMLHttpRequest();
savesettings.onreadystatechange = function () {
if (savesettings.readyState === 4) {
if (savesettings.status === 200) {
alert("Änderungen an settings.json gespeichert!");
} else if (savesettings.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savesettings.open("POST", "/admin/set_json_settings", true);
savesettings.send(document.getElementById("ex_settings").value);
}
};

View File

@ -0,0 +1,304 @@
var NamesEditor = {
//public variables
iconeditorcounter: 0,
filterGropus: { no: "immer Sichtbar", fw: "Feuerwehr", sani: "Sanitäter", pol: "Polizei", oa: "Ordnungsamt", si: "Sicherheitsdienst", thw: "Technisches Hilfswerk", crew: "Veranstalter", dev: "Entwickler" },
//public functions
Abort: function (el) {
el.parentNode.removeChild(el);
},
Add: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 55px;'/></td>";
newrow.innerHTML += "<td><input style='width: 245px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> wähle Icon</td>";
newrow.innerHTML += "<td>" + this.CreateSelectBox("", "item", { item: "" }, this.filterGropus, null, null, true);
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='NamesEditor.SaveRow(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("nametable").children[1].appendChild(newrow);
},
BuildIconJson: function (url) {
var query = this.SplitQueryIntoObject(this.SplitUrlIntoParts(url).query);
var markerobj = {};
if (Object.prototype.hasOwnProperty.call(query, "icon") && query["icon"] === "person") {
markerobj["person"] = {};
if (Object.prototype.hasOwnProperty.call(query, "person-org")) {
markerobj["person"]["org"] = query["person-org"];
}
if (Object.prototype.hasOwnProperty.call(query, "person-funct")) {
markerobj["person"]["funct"] = query["person-funct"];
}
if (Object.prototype.hasOwnProperty.call(query, "person-rang")) {
markerobj["person"]["rang"] = query["person-rang"];
}
if (Object.prototype.hasOwnProperty.call(query, "person-text")) {
markerobj["person"]["text"] = query["person-text"];
}
if (Object.prototype.hasOwnProperty.call(query, "person-typ")) {
if (Array.isArray(query["person-typ"])) {
markerobj["person"]["typ"] = new Array();
for (var i in query["person-typ"]) {
markerobj["person"]["typ"].push(query["person-typ"][i]);
}
} else {
markerobj["person"]["typ"] = new Array();
markerobj["person"]["typ"].push(query["person-typ"]);
}
}
}
return markerobj;
},
ChangeLinkPreview: function (key, val) {
var cur = this.SplitUrlIntoParts(document.getElementById("markerprev").data);
var query = this.SplitQueryIntoObject(cur.query);
if (typeof val === "object") {
query[key] = new Array();
for (var i = 0; i < val.length; i++) {
query[key].push(val[i].value);
if (val[i].value === "---") {
delete query[key];
break;
}
}
} else {
if (val === "---" || val === "") {
delete query[key];
} else {
query[key] = val;
}
}
document.getElementById("markerprev").data = cur.file + "?" + this.JoinObjectIntoQuery(query);
},
CreateSelectBox: function (title, key, query, options, muliple, group, noonchange) {
var html = title !== "" ? title + ": " : "";
var onchange = "";
if (!(typeof noonchange !== "undefined" && noonchange === true)) {
var eventtext = "NamesEditor.ChangeLinkPreview(\"" + key + "\",this.selectedOptions);";
if (typeof group !== "undefined" && group !== null) {
eventtext += " document.getElementById(\"" + group + "\"+this.value).style.display = \"block\";'";
}
onchange = " onchange='" + eventtext + "'";
}
html += "<select" + onchange + (typeof muliple !== "undefined" && muliple !== null ? " multiple" : "") + ">";
if (typeof muliple === "undefined" || muliple === null) {
html += "<option>---</option>";
}
for (var value in options) {
if (Object.prototype.hasOwnProperty.call(query, key) && query[key] === value) {
html += "<option value='" + value + "' selected>" + options[value] + "</option>";
} else if (Object.prototype.hasOwnProperty.call(query, key) && Array.isArray(query[key])) {
var notinqueryarray = true;
for (var i in query[key]) {
if (query[key][i] === value) {
notinqueryarray = false;
html += "<option value='" + value + "' selected>" + options[value] + "</option>";
}
}
if (notinqueryarray) {
html += "<option value='" + value + "'>" + options[value] + "</option>";
}
} else {
html += "<option value='" + value + "'>" + options[value] + "</option>";
}
}
html += "</select>";
return html;
},
Delete: function (el) {
var name = el.firstChild.innerHTML;
var answ = window.prompt("Wollen sie den Eintrag für \"" + name + "\" wirklich löschen?", "");
if (answ !== null) {
el.parentNode.removeChild(el);
}
},
Edit: function (el) {
var id = el.children[0].innerText;
var name = el.children[1].innerText;
var url = null;
var gfilter = el.children[3].attributes.rel.nodeValue;
if (el.children[2].children[0].hasAttribute("data")) {
url = el.children[2].children[0].data;
}
el.innerHTML = "<td><input style='width: 55px;' value='" + id + "'/></td>";
el.innerHTML += "<td><input style='width: 245px;' value='" + name + "'/></td>";
if (url === null) {
el.innerHTML += "<td><img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> wähle Icon</td>";
} else {
el.innerHTML += "<td><img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> <object data='" + url + "' type='image/svg+xml' style='height:50px; width:50px;'></object></td>";
}
el.innerHTML += "<td>" + this.CreateSelectBox("", "item", { item: gfilter }, this.filterGropus, null, null, true);
el.innerHTML += "<td><img src='../icons/general/save.png' onclick='NamesEditor.SaveRow(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
IconEditor: function (el) {
var url = "../api/svg/person.svg?";
el.id = "icon_edit_" + this.iconeditorcounter++;
if (el.children.length === 2) {
url = el.children[1].data;
}
var query = this.SplitQueryIntoObject(this.SplitUrlIntoParts(url).query);
var ie = document.createElement("div");
ie.id = "iconeditor";
ie.innerHTML = "<div class='innerbox'>" +
"<div class='preview'><object id='markerprev' data='" + url + "' type='image/svg+xml' style='height:200px; width:200px;'></object></div>" +
"<div class='controls'>" +
this.CreateSelectBox("Typ", "icon", query, { "person": "Person" }, null, "iconeditor-type-") + "<br>" +
"<div id='iconeditor-type-person' style='display: " + (Object.prototype.hasOwnProperty.call(query, "icon") && query["icon"] === "person" ? "block" : "none") + ";'>" +
this.CreateSelectBox("Organisation", "person-org", query, { "fw": "Feuerwehr", "thw": "Technisches Hilfswerk", "hilo": "Hilfsorganisationen, Bundeswehr", "fueh": "Einrichtungen der Führung", "pol": "Polizei, Bundespolizei, Zoll", "sonst": "Sonstige Einrichtungen der Gefahrenabwehr" }) + "<br>" +
this.CreateSelectBox("Funktion", "person-funct", query, { "sonder": "Sonder", "fueh": "Führung" }) + "<br>" +
this.CreateSelectBox("Rang", "person-rang", query, { "trupp": "Trupp", "grupp": "Gruppe", "zug": "Zug" }) + "<br>" +
"Text: <input onchange='NamesEditor.ChangeLinkPreview(\"person-text\",this.value);' value='" + (Object.prototype.hasOwnProperty.call(query, "person-text") ? query["person-text"] : "") + "'><br>" +
this.CreateSelectBox("Typ", "person-typ", query, { "loesch": "Brandbekämpfung/Löscheinsatz", "sani": "Rettungswesen, Sanitätswesen, Gesundheitswesen", "betreu": "Betreuung" }, true) + "<br>" +
"</div>" +
"</div>" +
"<div class='save'><button onclick='NamesEditor.SaveIconEditor(\"" + el.id + "\"); '>Schließen</botton></div>" +
"</div>";
document.getElementsByTagName("body")[0].appendChild(ie);
},
JoinObjectIntoQuery: function (queryobj) {
var query = new Array();
for (var id in queryobj) {
if (Array.isArray(queryobj[id])) {
for (var i in queryobj[id]) {
query.push(encodeURIComponent(id) + "=" + encodeURIComponent(queryobj[id][i]));
}
} else {
query.push(encodeURIComponent(id) + "=" + encodeURIComponent(queryobj[id]));
}
}
return query.join("&");
},
ParseIcon: function (markerobj) {
var url = "../api/svg/person.svg";
if (Object.prototype.hasOwnProperty.call(markerobj, "person")) {
url += "?icon=person";
if (Object.prototype.hasOwnProperty.call(markerobj["person"], "org")) {
url += "&person-org=" + markerobj["person"]["org"];
}
if (Object.prototype.hasOwnProperty.call(markerobj["person"], "funct")) {
url += "&person-funct=" + markerobj["person"]["funct"];
}
if (Object.prototype.hasOwnProperty.call(markerobj["person"], "rang")) {
url += "&person-rang=" + markerobj["person"]["rang"];
}
if (Object.prototype.hasOwnProperty.call(markerobj["person"], "text")) {
url += "&person-text=" + markerobj["person"]["text"];
}
if (Object.prototype.hasOwnProperty.call(markerobj["person"], "typ") && Array.isArray(markerobj["person"]["typ"])) {
for (i in markerobj["person"]["typ"]) {
url += "&person-typ=" + markerobj["person"]["typ"][i];
}
}
}
return "<object data='" + url + "' type='image/svg+xml' style='height:50px; width:50px;'></object>";
},
ParseJson: function (namesconfig) {
document.getElementById("content").innerHTML = "";
var html = "<div id='nameeditor'><div class='title'>Namenseinträge in den Einstellungen</div>";
html += "<table id='nametable' class='settingstable'>";
html += "<thead><tr><th width='60'>ID</th><th width='250'>Name</th><th width='65'>Icon</th><th width='150'>Filter Gruppe</th><th width='50'></th></tr></thead>";
html += "<tbody>";
for (var id in namesconfig) {
if (Object.prototype.hasOwnProperty.call(namesconfig, id)) {
var nameentry = namesconfig[id];
html += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + nameentry["name"] + "</td>";
if (Object.prototype.hasOwnProperty.call(nameentry, "marker.svg")) {
html += "<td>" + this.ParseIcon(nameentry["marker.svg"]) + "</td>";
} else if (Object.prototype.hasOwnProperty.call(nameentry, "icon")) {
html += "<td><img src='" + nameentry["icon"] + "'></td>";
} else {
html += "<td><img src='../js/leaflet/images/marker-icon.png'></td>";
}
var gfilter = typeof nameentry.Group === "undefined" ? "no" : nameentry.Group;
html += "<td rel='" + gfilter + "'>" + this.filterGropus[gfilter] + "</td>";
html += "<td><img src='../icons/general/edit.png' onclick='NamesEditor.Edit(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
}
html += "</tbody>";
html += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='NamesEditor.Add()' class='pointer'> <img src='../icons/general/save.png' onclick='NamesEditor.Save()' class='pointer'></td></tr></tfoot>";
html += "</table>";
document.getElementById("content").innerHTML = html + "</div>";
},
Save: function () {
var rows = document.getElementById("nametable").children[1].children;
var namejson = {};
for (var i = 0; i < rows.length; i++) {
if (rows[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
var id = rows[i].children[0].innerText;
var name = rows[i].children[1].innerText;
namejson[id] = { "name": name, "Group": rows[i].children[3].attributes.rel.nodeValue };
if (rows[i].children[2].children[0].hasAttribute("data")) {
namejson[id]["marker.svg"] = this.BuildIconJson(rows[i].children[2].children[0].data);
}
}
var savenames = new XMLHttpRequest();
savenames.onreadystatechange = function () {
if (savenames.readyState === 4) {
if (savenames.status === 200) {
alert("Änderungen gespeichert!");
} else if (savenames.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savenames.open("PUT", "/admin/api/json/name", true);
savenames.send(JSON.stringify(namejson));
},
SaveIconEditor: function (id) {
var cell = document.getElementById(id);
cell.innerHTML = "<img src='../icons/general/icon_edit.png' onclick='NamesEditor.IconEditor(this.parentNode)' class='pointer'> <object data='" + document.getElementById("markerprev").data + "' type='image/svg+xml' style='height:50px; width:50px;'></object>";
cell.removeAttribute("id");
document.getElementById("iconeditor").remove();
},
SaveRow: function (el) {
var id = el.children[0].children[0].value;
var name = el.children[1].children[0].value;
var url = null;
var gfilter = el.children[3].children[0].selectedOptions[0].value;
if (gfilter === "---") {
gfilter = "no";
}
if (el.children[2].children.length === 2) {
url = el.children[2].children[1].data;
}
el.innerHTML = "<td>" + id + "</td>" +
"<td>" + name + "</td>";
if (url === null) {
el.innerHTML += "<td><img src='../js/leaflet/images/marker-icon.png'></td>";
} else {
el.innerHTML += "<td><object data='" + url + "' type='image/svg+xml' style='height:50px; width:50px;'></object></td>";
}
el.innerHTML += "<td rel='" + gfilter + "'>" + this.filterGropus[gfilter] + "</td>";
el.innerHTML += "<td><img src='../icons/general/edit.png' onclick='NamesEditor.Edit(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='NamesEditor.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
SplitQueryIntoObject: function (query) {
if (query.indexOf("?") !== -1) {
query = query.split("?")[1];
}
var queryobj = {};
var pairs = query.split("&");
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split("=");
if (Object.prototype.hasOwnProperty.call(queryobj, decodeURIComponent(pair[0]))) {
if (Array.isArray(queryobj[decodeURIComponent(pair[0])])) {
queryobj[decodeURIComponent(pair[0])].push(decodeURIComponent(pair[1] || ""));
} else {
var tmp = queryobj[decodeURIComponent(pair[0])];
queryobj[decodeURIComponent(pair[0])] = new Array();
queryobj[decodeURIComponent(pair[0])].push(tmp);
queryobj[decodeURIComponent(pair[0])].push(decodeURIComponent(pair[1] || ""));
}
} else {
queryobj[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || "");
}
}
return queryobj;
},
SplitUrlIntoParts: function (url) {
var parts = url.split("?");
return { "file": parts[0], "query": parts[1] || "" };
}
};

View File

@ -0,0 +1,330 @@
var Settings = {
//public functions
Abort: function (el) {
el.parentNode.removeChild(el);
},
AddDensity: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 195px;'/></td>";
newrow.innerHTML += "<td><textarea style='width: 240px;height: 60px;'></textarea></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='Settings.SaveRowdensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("crowdtable").children[1].appendChild(newrow);
},
AddFight: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><textarea style='width: 240px;height: 60px;'></textarea></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='Settings.SaveRowfight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("fighttable").children[1].appendChild(newrow);
},
AddSensor: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><input style='width: 250px;'/></td>";
newrow.innerHTML += "<td><input style='width: 145px;'/></td>";
newrow.innerHTML += "<td><img src='../icons/general/save.png' onclick='Settings.SaveRowSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
document.getElementById("sensortable").children[1].appendChild(newrow);
},
Delete: function (el) {
var answ = window.prompt("Wollen sie den Eintrag für \"" + el.firstChild.innerHTML + "\" wirklich löschen?", "");
if (answ !== null) {
el.parentNode.removeChild(el);
}
},
EditDensity: function (el) {
el.innerHTML = "<td><input style='width: 145px;' value='" + el.children[0].innerText + "'/></td>" +
"<td><input style='width: 195px;' value='" + el.children[1].innerText + "'/></td>" +
"<td><textarea style='width: 240px;height: 60px;'>" + el.children[2].innerText + "</textarea></td>" +
"<td><input style='width: 145px;' value='" + el.children[3].innerText + "'/></td>" +
"<td><img src='../icons/general/save.png' onclick='Settings.SaveRowdensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
EditFight: function (el) {
el.innerHTML = "<td><input style='width: 145px;' value='" + el.children[0].innerText + "'/></td>" +
"<td><textarea style='width: 240px;height: 60px;'>" + el.children[1].innerText + "</textarea></td>" +
"<td><input style='width: 145px;' value='" + el.children[2].innerText + "'/></td>" +
"<td><input style='width: 145px;' value='" + el.children[3].innerText + "'/></td>" +
"<td><img src='../icons/general/save.png' onclick='Settings.SaveRowfight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
EditSensor: function (el) {
el.innerHTML = "<td><input style='width: 145px;' value='" + el.children[0].innerText + "'/></td>" +
"<td><input style='width: 145px;' value='" + el.children[1].innerText + "'/></td>" +
"<td><input style='width: 250px;' value='" + el.children[2].innerText + "'/></td>" +
"<td><input style='width: 145px;' value='" + el.children[3].innerText + "'/></td>" +
"<td><img src='../icons/general/save.png' onclick='Settings.SaveRowSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Abort(this.parentNode.parentNode)' class='pointer'></td>";
},
ParseJson: function (jsonsettings) {
if (typeof jsonsettings.StartPos === "undefined") {
jsonsettings.StartPos = { lat: 0, lon: 0 };
}
if (typeof jsonsettings.CellIds === "undefined") {
jsonsettings.CellIds = [];
}
if (typeof jsonsettings.GridRadius === "undefined") {
jsonsettings.GridRadius = 1000;
}
if (typeof jsonsettings.FightDedection === "undefined") {
jsonsettings.FightDedection = [];
}
if (typeof jsonsettings.CrwodDensity === "undefined") {
jsonsettings.CrwodDensity = [];
}
if (typeof jsonsettings.Counting === "undefined") {
jsonsettings.Counting = [];
}
if (typeof jsonsettings.Sensors === "undefined") {
jsonsettings.Sensors = [];
}
var html = "<div id='settingseditor'><div class='title'>Einstellungen</div>";
html += "<div class='startloc'>Startpunkt: <input value='" + jsonsettings.StartPos.lat + "' id='startlat'> Lat, <input value='" + jsonsettings.StartPos.lon + "' id='startlon'> Lon</div>";
html += "<div class='wetterwarnings'>CellId's für DWD-Wetterwarnungen: <input value='" + jsonsettings.CellIds.join(";") + "' id='wetterids'> (Trennen durch \";\", <a href='https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.html'>cap_warncellids_csv</a>)</div>";
html += "<div class='gridradius'>Radius für das Grid um den Startpunkt: <input value='" + jsonsettings.GridRadius + "' id='gridrad'>m</div>";
html += "<div class='fightdedection'>Fight Dedection Kameras: <br>" + this._renderFightDedection(jsonsettings.FightDedection) + "</div>";
html += "<div class='crowddensity'>Crowd Density Kameras: <br>" + this._renderCrowdDensity(jsonsettings.CrwodDensity) + "</div>";
html += "<div class='sensorsettings'>Sensors: <br>" + this._renderSensorSettings(jsonsettings.Sensors) + "</div>";
html += "<div class='savesettings'><img src='../icons/general/save.png' onclick='Settings.Save()' class='pointer'></div>";
document.getElementById("content").innerHTML = html + "</div>";
},
Save: function () {
var ret = {};
ret.StartPos = {};
ret.StartPos.lat = parseFloat(document.getElementById("startlat").value.replace(",", "."));
ret.StartPos.lon = parseFloat(document.getElementById("startlon").value.replace(",", "."));
ret.CellIds = document.getElementById("wetterids").value.split(";");
ret.GridRadius = parseInt(document.getElementById("gridrad").value);
var rowsf = document.getElementById("fighttable").children[1].children;
var fightjson = {};
for (var i = 0; i < rowsf.length; i++) {
if (rowsf[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
var id = rowsf[i].children[0].innerText;
var coords = rowsf[i].children[1].innerHTML.split("<br>");
var polyjson = [];
for (var j = 0; j < coords.length; j++) {
var coord = coords[j].split(";");
polyjson[j] = { "Lat": this._filterFloat(coord[0]), "Lon": this._filterFloat(coord[1]) };
}
fightjson[id] = { "Poly": polyjson, "Alias": rowsf[i].children[2].innerText, "Level": this._filterFloat(rowsf[i].children[3].innerText) };
}
ret.FightDedection = fightjson;
var rowsc = document.getElementById("crowdtable").children[1].children;
var crowdjson = {};
for (i = 0; i < rowsc.length; i++) {
if (rowsc[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
id = rowsc[i].children[0].innerText;
var num = this._filterFloat(rowsc[i].children[1].innerText);
coords = rowsc[i].children[2].innerHTML.split("<br>");
polyjson = [];
for (j = 0; j < coords.length; j++) {
coord = coords[j].split(";");
polyjson[j] = { "Lat": this._filterFloat(coord[0]), "Lon": this._filterFloat(coord[1]) };
}
crowdjson[id] = {
"Poly": polyjson,
"Count": num,
"Alias": rowsc[i].children[3].innerText
};
}
ret.CrwodDensity = crowdjson;
var rowss = document.getElementById("sensortable").children[1].children;
var sensorjson = {};
for (i = 0; i < rowss.length; i++) {
if (rowss[i].children[0].children.length === 1) {
alert("Bitte zuerst alle Zeilen speichern oder Löschen!");
return;
}
id = rowss[i].children[0].innerText;
coord = rowss[i].children[2].innerHTML.split(";");
sensorjson[id] = {
"Poly": {
"Lat": this._filterFloat(coord[0]),
"Lon": this._filterFloat(coord[1])
},
"Level": this._filterFloat(rowss[i].children[3].innerText),
"Alias": rowss[i].children[1].innerText
};
}
ret.Sensors = sensorjson;
var savesettings = new XMLHttpRequest();
savesettings.onreadystatechange = function () {
if (savesettings.readyState === 4) {
if (savesettings.status === 200) {
alert("Änderungen gespeichert!");
} else if (savesettings.status === 501) {
alert("Ein Fehler ist aufgetreten (invalid JSON)!");
}
}
};
savesettings.open("PUT", "/admin/api/json/setting", true);
savesettings.send(JSON.stringify(ret));
},
SaveRowdensity: function (el) {
var coords = el.children[2].children[0].value.replace(/\n/gi, "<br>");
var coordscheck = coords.split("<br>");
var fail = false;
for (var i = 0; i < coordscheck.length; i++) {
var coord = coordscheck[i].split(";");
if (coord.length !== 2) {
fail = true;
break;
}
if (isNaN(this._filterFloat(coord[0])) || isNaN(this._filterFloat(coord[1]))) {
fail = true;
break;
}
}
if (fail) {
alert("Die Eingabe der Koordinaten ist nicht Korrekt!\n\nBeispiel:\n50.7;7.8\n50.6;7.9");
return;
}
if (isNaN(this._filterFloat(el.children[1].children[0].value))) {
alert("Die Eingabe der Maximalen Anzahl der Personen ist nicht Korrekt, erwarte eine Zahl.");
return;
}
el.innerHTML = "<td>" + el.children[0].children[0].value + "</td>" +
"<td>" + el.children[1].children[0].value + "</td>" +
"<td>" + coords + "</td>" +
"<td>" + el.children[3].children[0].value + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditDensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
SaveRowfight: function (el) {
var coords = el.children[1].children[0].value.replace(/\n/gi, "<br>");
var coordscheck = coords.split("<br>");
var fail = false;
for (var i = 0; i < coordscheck.length; i++) {
var coord = coordscheck[i].split(";");
if (coord.length !== 2) {
fail = true;
break;
}
if (isNaN(this._filterFloat(coord[0])) || isNaN(this._filterFloat(coord[1]))) {
fail = true;
break;
}
}
if (isNaN(this._filterFloat(el.children[3].children[0].value))) {
alert("Die Eingabe des Alertlevel erwartet einen Float");
return;
}
if (fail) {
alert("Die Eingabe der Koordinaten ist nicht Korrekt!\n\nBeispiel:\n50.7;7.8\n50.6;7.9");
return;
}
el.innerHTML = "<td>" + el.children[0].children[0].value + "</td>" +
"<td>" + coords + "</td>" +
"<td>" + el.children[2].children[0].value + "</td>" +
"<td>" + this._filterFloat(el.children[3].children[0].value) + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditFight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
SaveRowSensor: function (el) {
var coords = el.children[2].children[0].value;
var coord = coords.split(";");
var fail = false;
if (coord.length !== 2) {
fail = true;
} else if (isNaN(this._filterFloat(coord[0])) || isNaN(this._filterFloat(coord[1]))) {
fail = true;
}
if (isNaN(this._filterFloat(el.children[3].children[0].value))) {
alert("Die Eingabe des Alertlevel erwartet einen Float");
return;
}
if (fail) {
alert("Die Eingabe der Koordinaten ist nicht Korrekt!\n\nBeispiel:\n50.7;7.8");
return;
}
el.innerHTML = "<td>" + el.children[0].children[0].value + "</td>" +
"<td>" + el.children[1].children[0].value + "</td>" +
"<td>" + coords + "</td>" +
"<td>" + this._filterFloat(el.children[3].children[0].value) + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>";
},
//private functions
_filterFloat: function (value) {
if (/^(-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/.test(value)) {
return Number(value);
}
return NaN;
},
_renderCrowdDensity: function (json) {
var ret = "";
ret += "<table id='crowdtable' class='settingstable'>";
ret += "<thead><tr><th width='150'>ID</th><th width='200'>Personenanzahl</th><th width='250'>Koordinaten</th><th width='150'>Alias</th><th width='50'></th></tr></thead>";
ret += "<tbody>";
for (var id in json) {
var coords = [];
for (var i = 0; i < json[id].Poly.length; i++) {
coords[i] = json[id].Poly[i].Lat + ";" + json[id].Poly[i].Lon;
}
ret += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + json[id].Count + "</td>" +
"<td>" + coords.join("<br>") + "</td>" +
"<td>" + json[id].Alias + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditDensity(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
ret += "</tbody>";
ret += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='Settings.AddDensity()' class='pointer'></td></tr></tfoot>";
ret += "</table>";
return ret;
},
_renderFightDedection: function (json) {
var ret = "";
ret += "<table id='fighttable' class='settingstable'>";
ret += "<thead><tr><th width='150'>ID</th><th width='250'>Koordinaten</th><th width='150'>Alias</th><th width='150'>Alertlimit</th><th width='50'></th></tr></thead>";
ret += "<tbody>";
for (var id in json) {
var coords = [];
for (var i = 0; i < json[id].Poly.length; i++) {
coords[i] = json[id].Poly[i].Lat + ";" + json[id].Poly[i].Lon;
}
ret += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + coords.join("<br>") + "</td>" +
"<td>" + json[id].Alias + "</td>" +
"<td>" + json[id].Level + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditFight(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
ret += "</tbody>";
ret += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='Settings.AddFight()' class='pointer'></td></tr></tfoot>";
ret += "</table>";
return ret;
},
_renderSensorSettings: function (json) {
var ret = "";
ret += "<table id='sensortable' class='settingstable'>";
ret += "<thead><tr><th width='150'>ID</th><th width='150'>Alias</th><th width='250'>Koordinaten</th><th width='150'>Warn above</th><th width='50'></th></tr></thead>";
ret += "<tbody>";
for (var id in json) {
ret += "<tr>" +
"<td>" + id + "</td>" +
"<td>" + json[id].Alias + "</td>" +
"<td>" + json[id].Poly.Lat + ";" + json[id].Poly.Lon + "</td>" +
"<td>" + json[id].Level + "</td>" +
"<td><img src='../icons/general/edit.png' onclick='Settings.EditSensor(this.parentNode.parentNode)' class='pointer'> <img src='../icons/general/remove.png' onclick='Settings.Delete(this.parentNode.parentNode)' class='pointer'></td>" +
"</tr>";
}
ret += "</tbody>";
ret += "<tfoot><tr><td></td><td></td><td></td><td></td><td><img src='../icons/general/add.png' onclick='Settings.AddSensor()' class='pointer'></td></tr></tfoot>";
ret += "</table>";
return ret;
},
};

View File

@ -141,7 +141,7 @@ object {
}
#pannels #pannels_pos .item .icon img {
height: 40px;
width: 40px;
width: 35px;
margin-left: 3px;
}
#pannels #pannels_pos .item .line1 .name {

View File

@ -1,62 +1,15 @@
var FunctionsObject = {
/// private variables
_internalTimeOffset: 0,
/// public functions
Start: function () {
setInterval(this._Runner60000, 60000);
setInterval(this._Runner1000, 1000);
this._Runner60000();
this._Runner1000();
this._RunnerOnce();
setInterval(this._QueryJsonEveryMinute, 60000);
setInterval(this._QueryJsonEverySecond, 1000);
this._QueryJsonEveryMinute();
this._QueryJsonEverySecond();
this._QueryJsonStartup();
return this;
},
_Runner60000: function () {
var get60000 = new XMLHttpRequest();
get60000.onreadystatechange = function () {
if (get60000.readyState === 4 && get60000.status === 200) {
var json = JSON.parse(get60000.responseText);
FunctionsObject._ParseAJAX(json["currenttime"]);
}
};
get60000.open("GET", "/get60000", true);
get60000.send();
},
_Runner1000: function () {
var get1000 = new XMLHttpRequest();
get1000.onreadystatechange = function () {
if (get1000.readyState === 4 && get1000.status === 200) {
var json = JSON.parse(get1000.responseText);
MarkerObject._ParseAJAXLoc(json["loc"]);
MarkerObject._ParseAJAXPanic(json["panic"]);
OverlayObject._ParseAJAXCount(json["cameracount"]);
OverlayObject._ParseAJAXDensity(json["crowdcount"]);
MarkerObject._ParseAJAXSensors(json["sensors"]);
MenuObject._ParseAJAXWeatherAlerts(json["weatherwarnings"]);
MapObject._ParseAJAXFightDedection(json["fightdedect"]);
MapObject._ParseAJAXDensity(json["crowdcount"]);
}
};
get1000.open("GET", "/get1000", true);
get1000.send();
},
_RunnerOnce: function () {
var getonce = new XMLHttpRequest();
getonce.onreadystatechange = function () {
if (getonce.readyState === 4 && getonce.status === 200) {
var json = JSON.parse(getonce.responseText);
MapObject._ParseAJAXLayers(json["getlayer"]);
MapObject._ParseAJAXGeo(json["getgeo"]);
MapObject._ParseAJAXSettings(json["startup"]);
OverlayObject._ParseAJAXSettings(json["startup"]);
MarkerObject._ParseAJAXSettings(json["startup"]);
}
};
getonce.open("GET", "/getonce", true);
getonce.send();
},
_ParseAJAX: function (utcobject) {
if (utcobject.hasOwnProperty("utc")) {
this._internalTimeOffset = Date.now() - Date.parse(utcobject["utc"]);
}
},
TimeCalculation: function (timestr, type) {
if (type === "diffraw" || type === "difftext" || type === "difftextn") {
var diff = Math.round((Date.now() - Date.parse(timestr) - this._internalTimeOffset) / 1000);
@ -86,5 +39,55 @@
var str = date.toLocaleString();
return str;
}
},
/// private functions
_ParseAJAX: function (utcobject) {
if (Object.prototype.hasOwnProperty.call(utcobject, "utc")) {
this._internalTimeOffset = Date.now() - Date.parse(utcobject["utc"]);
}
},
_QueryJsonEveryMinute: function () {
var get60000 = new XMLHttpRequest();
get60000.onreadystatechange = function () {
if (get60000.readyState === 4 && get60000.status === 200) {
var json = JSON.parse(get60000.responseText);
FunctionsObject._ParseAJAX(json);
}
};
get60000.open("GET", "/api/time", true);
get60000.send();
},
_QueryJsonEverySecond: function () {
var get1000 = new XMLHttpRequest();
get1000.onreadystatechange = function () {
if (get1000.readyState === 4 && get1000.status === 200) {
var json = JSON.parse(get1000.responseText);
MarkerObject.ParseAJAXPositionModel(json.position);
MarkerObject.ParseAJAXSensorModel(json.sensor);
OverlayObject.ParseAJAXCameraModel(json.camera);
MapObject.ParseAJAXCameraModel(json.camera);
MenuObject.ParseAJAXSensorModel(json.sensor);
}
};
get1000.open("GET", "/api/json/position,camera,sensor", true);
get1000.send();
},
_QueryJsonStartup: function () {
var getonce = new XMLHttpRequest();
getonce.onreadystatechange = function () {
if (getonce.readyState === 4 && getonce.status === 200) {
var json = JSON.parse(getonce.responseText);
MarkerObject.ParseAJAXSettings(json.settings);
OverlayObject.ParseAJAXSettings(json.settings);
MapObject.ParseAJAXSettings(json.settings);
}
};
getonce.open("GET", "/api/json/settings", true);
getonce.send();
}
}.Start();

View File

@ -1,20 +1,155 @@
var MapObject = {
Map: {},
/// public variables
GeoJson: {},
_FightDedection: {},
Map: {},
/// private variables
_DensityAreas: {},
_FightDedection: {},
_SpecialMarkers: new Array(),
/// public functions
JumpTo: function (lat, lon) {
this.Map.flyTo([lat, lon], 19);
},
ParseAJAXCameraModel: function (json) {
this._ParseAJAXFightDedection(json.Fights);
this._ParseAJAXDensity(json.Density);
},
ParseAJAXSettings: function (settings) {
this._ParseAJAXLayers(settings.Layers);
this._ParseAJAXGeo(settings.GeoLayer);
this.Map.panTo([settings.Startloclat, settings.Startloclon]);
this._GenerateGrid(settings.Grid);
this._GenerateFightBoxes(settings.FightDedection);
this._GenerateDensityBoxes(settings.DensityArea);
},
Start: function () {
this.Map = L.map('bigmap').setView([0, 0], 16);
this._SetupMapZoomFontsize();
this._SetupClickHandler();
return this;
},
_ParseAJAXSettings: function (settings) {
this.Map.panTo([settings.Startloclat, settings.Startloclon]);
this._GenerateGrid(settings.Grid);
this._GenerateFightBoxes(settings.FightDedection);
this._GenerateDensityBoxes(settings.DensityArea);
/// private functions
_createRGB: function (current, max) {
return "hsl(" + 120 * (1 - current / max) + ",100%,50%)";
},
_GenerateDensityBoxes: function (densityareas) {
for (var cameraid in densityareas) {
this._DensityAreas[cameraid] = { 'Poly': L.polygon(densityareas[cameraid].Polygon, { color: 'hsl(120,100%,50%)', weight: 1 }).addTo(this.Map), 'Maximum': densityareas[cameraid].Maximum };
this._DensityAreas[cameraid].Poly.bindPopup("<strong>Besuchermenge:</strong><br>" +
"Besucher <strong>(0/" + this._DensityAreas[cameraid].Maximum + ")</strong> Personen<br>" +
"<progress value='0' max='" + this._DensityAreas[cameraid].Maximum + "'></progress>");
}
},
_GenerateFightBoxes: function (fightdedection) {
for (var cameraid in fightdedection) {
this._FightDedection[cameraid] = {};
this._FightDedection[cameraid].Box = L.polygon(fightdedection[cameraid].Polygon, { color: 'black', weight: 1 }).addTo(this.Map);
this._FightDedection[cameraid].Box.bindPopup("Fightdedection " + fightdedection[cameraid].Alias);
this._FightDedection[cameraid].Level = fightdedection[cameraid].Level;
}
},
_GenerateGrid: function (grid) {
for (var i = 0; i < grid.Major.length; i++) {
var linemajor = grid.Major[i];
L.polyline([[linemajor.from[0], linemajor.from[1]], [linemajor.to[0], linemajor.to[1]]], { color: "red", weight: 1, interactive: false }).addTo(this.Map);
}
for (var j = 0; j < grid.Minor.length; j++) {
var lineminor = grid.Minor[j];
L.polyline([[lineminor.from[0], lineminor.from[1]], [lineminor.to[0], lineminor.to[1]]], { color: "red", weight: 0.7, opacity: 0.5, interactive: false }).addTo(this.Map);
}
},
_HidePanel: function (e) {
MenuObject.ShowHidePanel(null);
},
_ParseAJAXDensity: function (json) {
for (var cameraid in json) {
if (Object.prototype.hasOwnProperty.call(this._DensityAreas, cameraid)) {
var crowd = json[cameraid];
var box = this._DensityAreas[cameraid].Poly;
var max = this._DensityAreas[cameraid].Maximum;
var cur = crowd.DensityCount;
if (cur > max) {
cur = max;
}
box.setStyle({ color: this._createRGB(cur, max) });
var p = box.getPopup().setContent("<strong>Besuchermenge:</strong><br>" +
"Besucher <strong>(" + crowd.DensityCount + "/" + max + ")</strong> Personen<br>" +
"<progress value='" + cur + "' max='" + max + "'></progress>").update();
}
}
},
_ParseAJAXFightDedection: function (json) {
for (var cameraid in json) {
if (Object.prototype.hasOwnProperty.call(this._FightDedection, cameraid)) {
var fight = json[cameraid];
var box = this._FightDedection[cameraid].Box;
var diff = FunctionsObject.TimeCalculation(fight["LastUpdate"], "diffraw");
if (fight["FightProbability"] > this._FightDedection[cameraid].Level) {
if (diff <= 10 && box.options.color === "black") {
box.setStyle({ color: 'rgb(' + fight["FightProbability"] * 255 + ',0,0)' });
} else if (diff <= 10 && box.options.color !== "black") {
if (diff % 2 === 0) {
box.setStyle({ color: 'rgb(' + fight["FightProbability"] * 255 + ',0,0)' });
} else {
box.setStyle({ color: 'green' });
}
} else if (diff > 10 && box.options.color !== "black") {
box.setStyle({ color: 'black' });
}
}
}
}
},
_ParseAJAXGeo: function (geo) {
if (!(Object.keys(geo).length === 0 && geo.constructor === Object)) {
this.GeoJson = geo;
L.geoJSON(geo, {
style: function (features) {
return {
color: typeof features.properties["stroke"] === "undefined" ? '#000000' : features.properties["stroke"],
weight: typeof features.properties["stroke-width"] === "undefined" ? 1 : features.properties["stroke-width"],
opacity: typeof features.properties["stroke-opacity"] === "undefined" ? 1 : features.properties["stroke-opacity"],
fillColor: typeof features.properties["fill"] === "undefined" ? '#ffffff' : features.properties["fill"],
fillOpacity: typeof features.properties["fill-opacity"] === "undefined" ? 1 : features.properties["fill-opacity"]
};
},
onEachFeature: function (feature, layer) {
if (feature.geometry.type === "Polygon" || feature.geometry.type === "Point" && Object.prototype.hasOwnProperty.call(feature.properties, "icon")) {
var text = "<b>" + feature.properties.name + "</b>";
if (Object.prototype.hasOwnProperty.call(feature.properties, "description")) {
text = text + "<br>" + feature.properties.description;
}
layer.bindPopup(text, { maxWidth: 485 });
}
},
pointToLayer: function (geoJsonPoint, latlng) {
if (Object.prototype.hasOwnProperty.call(geoJsonPoint.properties, "description") && geoJsonPoint.properties["description"] === "snumber" && !Object.prototype.hasOwnProperty.call(geoJsonPoint.properties, "icon")) {
var snumbericon = L.marker(latlng, {
icon: new L.DivIcon({
className: "snumber-icon",
html: geoJsonPoint.properties["name"],
iconSize: [8, 8]
}),
interactive: false
});
MapObject._SpecialMarkers.push(snumbericon);
return snumbericon;
} else if (Object.prototype.hasOwnProperty.call(geoJsonPoint.properties, "description") && geoJsonPoint.properties["description"] === "coord" && !Object.prototype.hasOwnProperty.call(geoJsonPoint.properties, "icon")) {
var coordicon = L.marker(latlng, {
icon: new L.DivIcon({
className: "coord-icon",
html: geoJsonPoint.properties["name"]
}),
interactive: false
});
MapObject._SpecialMarkers.push(coordicon);
return coordicon;
} else if (Object.prototype.hasOwnProperty.call(geoJsonPoint.properties, "icon")) {
return L.marker(latlng, { icon: L.icon({ iconUrl: "css/icons/cctv.png", iconSize: [32, 32] }) });
}
}
}).addTo(this.Map);
}
},
_ParseAJAXLayers: function (maps) {
var i = 0;
@ -43,7 +178,7 @@
}
}
for (key in maps) {
if (!baseMaps.hasOwnProperty(maps[key]["title"])) {
if (!Object.prototype.hasOwnProperty.call(baseMaps, maps[key]["title"])) {
baseMaps[maps[key]["title"]] = L.tileLayer(maps[key]["url"], {
attribution: maps[key]["attribution"],
minZoom: maps[key]["minZoom"],
@ -55,124 +190,8 @@
L.control.layers(baseMaps).addTo(this.Map);
}
},
_GenerateGrid: function (grid) {
for (var i = 0; i < grid.Major.length; i++) {
var linemajor = grid.Major[i];
L.polyline([[linemajor.from[0], linemajor.from[1]], [linemajor.to[0], linemajor.to[1]]], { color: "red", weight: 1, interactive: false }).addTo(this.Map);
}
for (var j = 0; j < grid.Minor.length; j++) {
var lineminor = grid.Minor[j];
L.polyline([[lineminor.from[0], lineminor.from[1]], [lineminor.to[0], lineminor.to[1]]], { color: "red", weight: 0.7, opacity: 0.5, interactive: false }).addTo(this.Map);
}
},
_GenerateFightBoxes: function (fightdedection) {
for (var cameraid in fightdedection) {
this._FightDedection[cameraid] = {};
this._FightDedection[cameraid].Box = L.polygon(fightdedection[cameraid].Polygon, { color: 'black', weight: 1 }).addTo(this.Map);
this._FightDedection[cameraid].Box.bindPopup("Fightdedection " + fightdedection[cameraid].Alias);
this._FightDedection[cameraid].Level = fightdedection[cameraid].Level;
}
},
_ParseAJAXFightDedection: function (json) {
for (var cameraid in json) {
if (this._FightDedection.hasOwnProperty(cameraid)) {
var fight = json[cameraid];
var box = this._FightDedection[cameraid].Box;
var diff = FunctionsObject.TimeCalculation(fight["LastUpdate"], "diffraw");
if (fight["FightProbability"] > this._FightDedection[cameraid].Level) {
if (diff <= 10 && box.options.color === "black") {
box.setStyle({ color: 'rgb(' + fight["FightProbability"] * 255 + ',0,0)' });
} else if (diff <= 10 && box.options.color !== "black") {
if (diff % 2 === 0) {
box.setStyle({ color: 'rgb(' + fight["FightProbability"] * 255 + ',0,0)' });
} else {
box.setStyle({ color: 'green' });
}
} else if (diff > 10 && box.options.color !== "black") {
box.setStyle({ color: 'black' });
}
}
}
}
},
_GenerateDensityBoxes: function (densityareas) {
for (var cameraid in densityareas) {
this._DensityAreas[cameraid] = { 'Poly': L.polygon(densityareas[cameraid].Polygon, { color: 'hsl(120,100%,50%)', weight: 1 }).addTo(this.Map), 'Maximum': densityareas[cameraid].Maximum };
this._DensityAreas[cameraid].Poly.bindPopup("<strong>Besuchermenge:</strong><br>" +
"Besucher <strong>(0/" + this._DensityAreas[cameraid].Maximum + ")</strong> Personen<br>" +
"<progress value='0' max='" + this._DensityAreas[cameraid].Maximum + "'></progress>");
}
},
_ParseAJAXDensity: function (json) {
for (var cameraid in json) {
if (this._DensityAreas.hasOwnProperty(cameraid)) {
var crowd = json[cameraid];
var box = this._DensityAreas[cameraid].Poly;
var max = this._DensityAreas[cameraid].Maximum;
var cur = crowd.DensityCount;
if (cur > max) {
cur = max;
}
box.setStyle({ color: this._createRGB(cur, max) });
var p = box.getPopup().setContent("<strong>Besuchermenge:</strong><br>" +
"Besucher <strong>(" + crowd.DensityCount + "/" + max + ")</strong> Personen<br>" +
"<progress value='" + cur + "' max='" + max + "'></progress>").update();
}
}
},
_createRGB: function (current, max) {
return "hsl(" + 120 * (1 - current / max) + ",100%,50%)";
},
_ParseAJAXGeo: function (geo) {
if (!(Object.keys(geo).length === 0 && geo.constructor === Object)) {
this.GeoJson = geo;
L.geoJSON(geo, {
style: function (features) {
return {
color: typeof features.properties["stroke"] === "undefined" ? '#000000' : features.properties["stroke"],
weight: typeof features.properties["stroke-width"] === "undefined" ? 1 : features.properties["stroke-width"],
opacity: typeof features.properties["stroke-opacity"] === "undefined" ? 1 : features.properties["stroke-opacity"],
fillColor: typeof features.properties["fill"] === "undefined" ? '#ffffff' : features.properties["fill"],
fillOpacity: typeof features.properties["fill-opacity"] === "undefined" ? 1 : features.properties["fill-opacity"]
};
},
onEachFeature: function (feature, layer) {
if (feature.geometry.type === "Polygon" || feature.geometry.type === "Point" && feature.properties.hasOwnProperty("icon")) {
var text = "<b>" + feature.properties.name + "</b>";
if (feature.properties.hasOwnProperty("description")) {
text = text + "<br>" + feature.properties.description;
}
layer.bindPopup(text, { maxWidth: 485 });
}
},
pointToLayer: function (geoJsonPoint, latlng) {
if (geoJsonPoint.properties.hasOwnProperty("description") && geoJsonPoint.properties["description"] === "snumber" && !geoJsonPoint.properties.hasOwnProperty("icon")) {
var snumbericon = L.marker(latlng, {
icon: new L.DivIcon({
className: "snumber-icon",
html: geoJsonPoint.properties["name"],
iconSize: [8, 8]
}),
interactive: false
});
MapObject._SpecialMarkers.push(snumbericon);
return snumbericon;
} else if (geoJsonPoint.properties.hasOwnProperty("description") && geoJsonPoint.properties["description"] === "coord" && !geoJsonPoint.properties.hasOwnProperty("icon")) {
var coordicon = L.marker(latlng, {
icon: new L.DivIcon({
className: "coord-icon",
html: geoJsonPoint.properties["name"]
}),
interactive: false
});
MapObject._SpecialMarkers.push(coordicon);
return coordicon;
} else if (geoJsonPoint.properties.hasOwnProperty("icon")) {
return L.marker(latlng, { icon: L.icon({ iconUrl: "css/icons/cctv.png", iconSize: [32, 32] }) });
}
}
}).addTo(this.Map);
}
_SetupClickHandler: function () {
this.Map.on("click", this._HidePanel);
},
_SetupMapZoomFontsize: function () {
this.Map.on('zoomend', function () {
@ -257,14 +276,5 @@
}
MarkerObject.ScaleSensors("zoom");
});
},
_SetupClickHandler: function () {
this.Map.on("click", this._HidePanel);
},
_HidePanel: function (e) {
MenuObject.ShowHidePanel(null);
},
JumpTo: function (lat, lon) {
this.Map.flyTo([lat, lon], 19);
}
}.Start();

View File

@ -1,20 +1,65 @@
var MarkerObject = {
_Markers: {},
PanicData: {},
/// public variables
LocationData: {},
PanicData: {},
VisibleMarkers: {},
/// private variables
_Markers: {},
_Sensors: {},
_SensorSettings: {},
/// public functions
ChangeFilter: function (select) {
this.VisibleMarkers = {};
if (select.selectedOptions.length > 0) {
for (var i = 0; i < select.selectedOptions.length; i++) {
this.VisibleMarkers[select.selectedOptions[i].value] = true;
}
this.VisibleMarkers["no"] = true;
this.VisibleMarkers["___isset"] = true;
}
this._ParseAJAXLoc(this.LocationData);
},
ParseAJAXPositionModel: function (json) {
this._ParseAJAXLoc(json.Positions);
this._ParseAJAXPanic(json.Alarms);
},
ParseAJAXSensorModel: function (json) {
this._ParseAJAXSensors(json.Enviroments);
},
ParseAJAXSettings: function (json) {
this._SensorSettings = json["Sensors"];
},
ScaleSensors: function (el) {
if (el === "zoom") {
for (var sensorid in this._Sensors) {
this.ScaleSensors(document.getElementById('MapSensor_id_' + sensorid));
}
return;
}
var currentZoom = MapObject.Map.getZoom();
var scale = 1;
if (currentZoom < 14) {
scale = 0;
} else if (currentZoom === 14) {
scale = 0.2;
} else if (currentZoom === 15) {
scale = 0.5;
} else if (currentZoom >= 16) {
scale = 1;
}
el.style.cssText = "transform: scale(" + scale + ");";
},
Start: function () {
return this;
},
/// private functions
_ParseAJAXLoc: function (serverLocation) {
this.LocationData = serverLocation;
for (var key in this.LocationData) {
if (this.LocationData.hasOwnProperty(key)) {
if (Object.prototype.hasOwnProperty.call(this.LocationData, key)) {
var positionItem = this.LocationData[key];
if (positionItem['Latitude'] !== 0 || positionItem['Longitude'] !== 0) {
if (!this._Markers.hasOwnProperty(key)) {
if (!Object.prototype.hasOwnProperty.call(this._Markers, key)) {
var marker = null;
if (positionItem['Icon'] === null) {
marker = L.marker([positionItem['Latitude'], positionItem['Longitude']], { 'title': positionItem['Name'] });
@ -51,7 +96,7 @@
}
}
}
if (positionItem.Group !== null && this.VisibleMarkers.hasOwnProperty("___isset") && !this.VisibleMarkers.hasOwnProperty(positionItem.Group)) {
if (positionItem.Group !== null && Object.prototype.hasOwnProperty.call(this.VisibleMarkers, "___isset") && !Object.prototype.hasOwnProperty.call(this.VisibleMarkers, positionItem.Group)) {
this._Markers[key]._icon.style.opacity = 0;
} else {
var lasttime = FunctionsObject.TimeCalculation(positionItem['Recievedtime'], "diffraw");
@ -76,11 +121,11 @@
_ParseAJAXPanic: function (serverPanic) {
this.PanicData = serverPanic;
for (var id in this.PanicData) {
if (this.PanicData.hasOwnProperty(id)) {
if (Object.prototype.hasOwnProperty.call(this.PanicData, id)) {
var alertItem = this.PanicData[id];
if (this._Markers.hasOwnProperty(id)) {
if (Object.prototype.hasOwnProperty.call(this._Markers, id)) {
var marker = this._Markers[id];
if (!(this.LocationData[id].Group !== null && this.VisibleMarkers.hasOwnProperty("___isset") && !this.VisibleMarkers.hasOwnProperty(this.LocationData[id].Group))) {
if (!(this.LocationData[id].Group !== null && Object.prototype.hasOwnProperty.call(this.VisibleMarkers, "___isset") /**/ && !Object.prototype.hasOwnProperty.call(this.VisibleMarkers, this.LocationData[id].Group))) {
if (FunctionsObject.TimeCalculation(alertItem["Recievedtime"], "diffraw") <= 10 && marker._icon.className.indexOf(" marker-alert") === -1) {
marker._icon.className += " marker-alert";
MenuObject.ShowMarkerInfoPerId(id);
@ -94,11 +139,11 @@
},
_ParseAJAXSensors: function (sensorjson) {
for (var sensorid in sensorjson) {
if (sensorjson.hasOwnProperty(sensorid)) {
if (this._SensorSettings.hasOwnProperty(sensorid)) {
if (Object.prototype.hasOwnProperty.call(sensorjson, sensorid)) {
if (Object.prototype.hasOwnProperty.call(this._SensorSettings, sensorid)) {
var sensordata = sensorjson[sensorid];
var sensorsettings = this._SensorSettings[sensorid];
if (!this._Sensors.hasOwnProperty(sensorid)) { //Sensor is not drawn until now
if (!Object.prototype.hasOwnProperty.call(this._Sensors, sensorid)) { //Sensor is not drawn until now
var sensor = null;
var sensorIcon = L.divIcon({
className: 'sensoricon',
@ -122,39 +167,5 @@
}
}
}
},
ScaleSensors: function (el) {
if (el === "zoom") {
for (var sensorid in this._Sensors) {
this.ScaleSensors(document.getElementById('MapSensor_id_' + sensorid));
}
return;
}
var currentZoom = MapObject.Map.getZoom();
var scale = 1;
if (currentZoom < 14) {
scale = 0;
} else if (currentZoom === 14) {
scale = 0.2;
} else if (currentZoom === 15) {
scale = 0.5;
} else if (currentZoom >= 16) {
scale = 1;
}
el.style.cssText = "transform: scale(" + scale + ");";
},
_ParseAJAXSettings: function(json) {
this._SensorSettings = json["Sensors"];
},
ChangeFilter: function (select) {
this.VisibleMarkers = {};
if (select.selectedOptions.length > 0) {
for (var i = 0; i < select.selectedOptions.length; i++) {
this.VisibleMarkers[select.selectedOptions[i].value] = true;
}
this.VisibleMarkers["no"] = true;
this.VisibleMarkers["___isset"] = true;
}
this._ParseAJAXLoc(this.LocationData);
}
}.Start();

View File

@ -1,9 +1,32 @@
var MenuObject = {
/// public variables
statusToDevice: null,
_visiblePanel: null,
/// private variables
_overviewStatus: new Array(),
Start: function () {
return this;
_visiblePanel: null,
/// public functions
ParseAJAXSensorModel: function (json) {
this._ParseAJAXWeatherAlerts(json.Weather.Warnungen);
},
SearchInGeoJson: function (searchtext) {
var html = "";
if (MapObject.GeoJson.features.length > 0) {
for (var i = 0; i < MapObject.GeoJson.features.length; i++) {
var feature = MapObject.GeoJson.features[i];
if ((feature.properties.name.toLowerCase().indexOf(searchtext.toLowerCase()) !== -1 ||
(typeof feature.properties.description !== "undefined" && feature.properties.description.toLowerCase().indexOf(searchtext.toLowerCase()) !== -1)) &&
feature.geometry.type === "Polygon") {
if (feature.geometry.coordinates.length > 0 && feature.geometry.coordinates[0].length > 0 && feature.geometry.coordinates[0][0].length > 0) {
html += "<div class='result' onclick='MapObject.JumpTo(" + feature.geometry.coordinates[0][0][1] + "," + feature.geometry.coordinates[0][0][0] + ");'><span class='text'>" +
"<span class='title'>" + feature.properties.name + "</span>" +
"<span class='desc'>" + (typeof feature.properties.description !== "undefined" ? feature.properties.description : "") + "</span></span>" +
"<span class='box' style='background-color: " + feature.properties.fill + "; border-color: " + feature.properties.stroke + "'></span>" +
"</div>";
}
}
}
}
document.getElementById("search_results").innerHTML = html;
},
ShowHidePanel: function (name) {
if (this._visiblePanel === null && name !== null) {
@ -32,15 +55,29 @@
this.statusToDevice = id;
this.ShowHidePanel("pannels_info");
},
Start: function () {
return this;
},
SubmitLoginForm: function () {
var adminlogin = new XMLHttpRequest();
adminlogin.onreadystatechange = function () {
if (adminlogin.readyState === 4 && adminlogin.status === 200) {
MenuObject._Update_pannels_admin();
}
};
adminlogin.open("POST", "/admin/login", true);
adminlogin.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
adminlogin.send("user=" + encodeURI(document.getElementById("pannels_admin_name").value) + "&pass=" + encodeURI(document.getElementById("pannels_admin_pass").value));
},
UpdateStatus: function () {
for (var id in MarkerObject.LocationData) {
if (MarkerObject.LocationData.hasOwnProperty(id)) {
if (Object.prototype.hasOwnProperty.call(MarkerObject.LocationData, id)) {
var positionItem = MarkerObject.LocationData[id];
if (typeof this._overviewStatus[id] === "undefined") {
this._overviewStatus[id] = this._CreateOverviewElement(positionItem, id);
document.getElementById("pannels_pos").appendChild(this._overviewStatus[id]);
}
if (positionItem.Group !== null && MarkerObject.VisibleMarkers.hasOwnProperty("___isset") && !MarkerObject.VisibleMarkers.hasOwnProperty(positionItem.Group)) {
if (positionItem.Group !== null && Object.prototype.hasOwnProperty.call(MarkerObject.VisibleMarkers, "___isset") && !Object.prototype.hasOwnProperty.call(MarkerObject.VisibleMarkers, positionItem.Group)) {
if (this._overviewStatus[id].className.indexOf("filter") === -1) {
this._overviewStatus[id].className = "item filter";
}
@ -51,8 +88,9 @@
}
this._UpdateOverviewElement(positionItem, id);
}
}
}
},
/// private functions
_UpdateOverviewElement: function (positionItem, id) {
if (positionItem["Batterysimple"] === 0) {
document.getElementById("overview-color-id-" + id).style.backgroundColor = "red";
@ -62,7 +100,9 @@
document.getElementById("overview-color-id-" + id).style.backgroundColor = "green";
}
document.getElementById("overview-name-id-" + id).innerText = positionItem["Name"];
document.getElementById("overview-akkuimg-id-" + id).src = "icons/akku/" + positionItem["Batterysimple"] + "-4.png";
if (document.getElementById("overview-akkuimg-id-" + id).src.substring(document.getElementById("overview-akkuimg-id-" + id).src.indexOf("/", 7) + 1) !== "icons/akku/" + positionItem["Batterysimple"] + "-4.png") {
document.getElementById("overview-akkuimg-id-" + id).src = "icons/akku/" + positionItem["Batterysimple"] + "-4.png";
}
if (positionItem["Fix"]) {
document.getElementById("overview-gps-id-" + id).innerText = "GPS-Empfang";
document.getElementById("overview-gps-id-" + id).style.color = "green";
@ -78,11 +118,11 @@
}
} else {
if (document.getElementById("overview-icon-id-" + id).children[0].hasAttribute("src")) {
if (document.getElementById("overview-icon-id-" + id).children[0]["src"].substring(document.getElementById("overview-icon-id-" + id).children[0]["src"].indexOf("/", 7) + 1) !== positionItem['Icon'] + "&marker-bg=hidden") {
document.getElementById("overview-icon-id-" + id).children[0]["src"] = positionItem['Icon'] + "&marker-bg=hidden";
if (document.getElementById("overview-icon-id-" + id).children[0]["src"].substring(document.getElementById("overview-icon-id-" + id).children[0]["src"].indexOf("/", 7) + 1) !== positionItem['MenuIcon']) {
document.getElementById("overview-icon-id-" + id).children[0]["src"] = positionItem['MenuIcon'];
}
} else {
document.getElementById("overview-icon-id-" + id).innerHTML = "<img src=\"" + positionItem['Icon'] + "&marker-bg=hidden" + "\" rel='svg'/>";
document.getElementById("overview-icon-id-" + id).innerHTML = "<img src=\"" + positionItem['MenuIcon'] + "\" rel='svg'/>";
}
}
},
@ -96,7 +136,7 @@
divItem.setAttribute("rel", id);
divItem.innerHTML = "<span class=\"color\" id=\"overview-color-id-" + id + "\"></span>";
if (positionItem['Icon'] !== null) {
divItem.innerHTML += "<span class=\"icon\" id=\"overview-icon-id-" + id + "\"><img src=\"" + positionItem['Icon'] + "&marker-bg=hidden" + "\" rel='svg'/></span>";
divItem.innerHTML += "<span class=\"icon\" id=\"overview-icon-id-" + id + "\"><img src=\"" + positionItem['MenuIcon'] + "\" rel='svg'/></span>";
} else {
divItem.innerHTML += "<span class=\"icon\" id=\"overview-icon-id-" + id + "\"><img src=\"icons/marker/map-marker.png\" /></span>";
}
@ -119,20 +159,9 @@
document.getElementById("pannels_admin").innerHTML = "<a href='/admin/' target='_blank'>Adminpannel</a>";
}
},
SubmitLoginForm: function () {
var adminlogin = new XMLHttpRequest();
adminlogin.onreadystatechange = function () {
if (adminlogin.readyState === 4 && adminlogin.status === 200) {
MenuObject._Update_pannels_admin();
}
};
adminlogin.open("POST", "/admin/login", true);
adminlogin.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
adminlogin.send("user=" + encodeURI(document.getElementById("pannels_admin_name").value) + "&pass=" + encodeURI(document.getElementById("pannels_admin_pass").value));
},
_Update_pannels_info: function () {
document.getElementById("pannels_info").innerHTML = "";
if (MarkerObject.LocationData.hasOwnProperty(this.statusToDevice)) {
if (Object.prototype.hasOwnProperty.call(MarkerObject.LocationData, this.statusToDevice)) {
var positionItem = MarkerObject.LocationData[this.statusToDevice];
var html = "<div class=\"name\">Name: <span class=\"bold\">" + positionItem["Name"] + "</span></div>";
html += "<div class=\"batt\"><span class=\"bold\">Batterie:</span> " + positionItem["Battery"] + "V <img src=\"icons/akku/" + positionItem["Batterysimple"] + "-4.png\"></div>";
@ -148,7 +177,7 @@
html += "<div class=\"lastgps\"><span class=\"bold\">Letzter Wert:</span> Vor: " + FunctionsObject.TimeCalculation(positionItem["Lastgpspostime"], "difftext") + "</div>";
html += "<div class=\"update\"><span class=\"bold\">Update:</span> " + FunctionsObject.TimeCalculation(positionItem["Recievedtime"], "str") + "<br><span class=\"bold\">Vor:</span> " + FunctionsObject.TimeCalculation(positionItem["Recievedtime"], "difftext") + "</div>";
html += "<div><span class=\"bold\">RSSI:</span> " + positionItem["Rssi"] + ", <span class=\"bold\">SNR:</span> " + positionItem["Snr"] + "</div>";
if (MarkerObject.PanicData.hasOwnProperty(this.statusToDevice)) {
if (Object.prototype.hasOwnProperty.call(MarkerObject.PanicData, this.statusToDevice)) {
var panicData = MarkerObject.PanicData[this.statusToDevice];
if (panicData["ButtonPressed"].length > 0) {
html += "<div class='alerts'><span class=\"bold\">Alerts:</span>";
@ -186,7 +215,7 @@
if (FunctionsObject.TimeCalculation(walert.From, "diffraw") < 0) {
html += "in " + FunctionsObject.TimeCalculation(walert.From, "difftextn");
} else {
html += "vor " + FunctionsObject.TimeCalculation(walert.From, "difftext");
html += "seit " + FunctionsObject.TimeCalculation(walert.From, "difftext");
}
html += " <b>Bis:</b> in " + FunctionsObject.TimeCalculation(walert.To, "difftextn") + "</span>" +
"</div>";
@ -197,25 +226,5 @@
document.getElementById("pannels_weather").innerHTML = "<h1>Keine Gefahren</h1>";
document.getElementById("menucol_weather_icon").className = "weather";
}
},
SearchInGeoJson: function (searchtext) {
var html = "";
if (MapObject.GeoJson.features.length > 0) {
for (var i = 0; i < MapObject.GeoJson.features.length; i++) {
var feature = MapObject.GeoJson.features[i];
if ((feature.properties.name.toLowerCase().indexOf(searchtext.toLowerCase()) !== -1 ||
(typeof feature.properties.description !== "undefined" && feature.properties.description.toLowerCase().indexOf(searchtext.toLowerCase()) !== -1)) &&
feature.geometry.type === "Polygon") {
if (feature.geometry.coordinates.length > 0 && feature.geometry.coordinates[0].length > 0 && feature.geometry.coordinates[0][0].length > 0) {
html += "<div class='result' onclick='MapObject.JumpTo(" + feature.geometry.coordinates[0][0][1] + "," + feature.geometry.coordinates[0][0][0]+");'><span class='text'>" +
"<span class='title'>" + feature.properties.name + "</span>" +
"<span class='desc'>" + (typeof feature.properties.description !== "undefined" ? feature.properties.description : "") + "</span></span>" +
"<span class='box' style='background-color: " + feature.properties.fill + "; border-color: " + feature.properties.stroke + "'></span>" +
"</div>";
}
}
}
}
document.getElementById("search_results").innerHTML = html;
}
}.Start();

View File

@ -1,12 +1,22 @@
var OverlayObject = {
/// private variables
_DensitySettings: {},
/// public functions
ParseAJAXCameraModel: function (json) {
this._ParseAJAXCount(json.Counter);
this._ParseAJAXDensity(json.Density);
},
ParseAJAXSettings: function (json) {
this._DensitySettings = json["DensityArea"];
},
Start: function () {
return this;
},
/// private functions
_ParseAJAXCount: function (cameracounts) {
var camerastext = "";
for (var cameraid in cameracounts) {
if (cameracounts.hasOwnProperty(cameraid)) {
if (Object.prototype.hasOwnProperty.call(cameracounts, cameraid)) {
var camera = cameracounts[cameraid];
var cameratext = "<div class='camera'>";
cameratext += "<span class='name'>" + cameraid + "</span>";
@ -22,8 +32,8 @@
_ParseAJAXDensity: function (cameradensy) {
var densystext = "";
for (var densyid in cameradensy) {
if (cameradensy.hasOwnProperty(densyid)) {
if (this._DensitySettings.hasOwnProperty(densyid)) {
if (Object.prototype.hasOwnProperty.call(cameradensy, densyid)) {
if (Object.prototype.hasOwnProperty.call(this._DensitySettings, densyid)) {
var densy = cameradensy[densyid];
var densytext = "<div class='camera'>";
densytext += "<span class='name'>" + this._DensitySettings[densyid].Alias + "</span>";
@ -34,8 +44,5 @@
}
}
document.getElementById("crwoddensy").innerHTML = densystext;
},
_ParseAJAXSettings: function (json) {
this._DensitySettings = json["DensityArea"];
}
}.Start();

BIN
doc/Details.pdn Normal file

Binary file not shown.

BIN
doc/Filter-Select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

99
doc/Manual.md Normal file
View File

@ -0,0 +1,99 @@
# Lora-Map
Lora-Map ist eine Karte die GPS-Positionen von Geräten anzeigt.
![Global mit Anmerkungen](Global.png)
## Bedienung
In diesem abschnitt findet sich eine Bedienungsnleitung über alle Funktionen der Karte.
### 1. Zoombuttons
![Zoombuttons](Zoombuttons.png)
Diese Knöpfe werden zum Steuern der Karte verwendet. Dabei kann mit `+` in die Karte hineingezoomt werden
und mit `-` aus der Karte herraus.
### 2. Tracker
![Trackerbutton](Tracker.png)
Über diesen Schalter lassen sich Informationen über alle, sowie über einzelne Tracker abrufen.
#### 2.1 Gesamtliste
![Gesamtliste](Gesamtliste.png)
Hier wird eine Übersicht über alle aktiven Tracker angezeigt. Diese können angeklickt werden um Details zu
diesen zu sehen (siehe Abschnitt 2.2).
##### 2.1.1 Batteriestatus
Der horizontale Balken in `Grün`, `Gelb` oder `Rot` zeigt den Akkuzustand schnell erkenntlich an. Grüne
Tracker sind voll, Gelbe etwa halbvoll und Rote sollen geladen werden.
##### 2.1.2 Icon
Hier wird das Icon des Trackers dargestellt.
##### 2.1.3 Name
Hier erscheint der Eingetragene `Name` des Trackers. Dies kann ein Funkrufname oder auch ein anderes Kürzel
sein. Die Anzahl der Zeichen ist begrenzt, da dieser Name auch auf der Karte über den Symbolen erscheint.
##### 2.1.4 Akkusymbol
Hier wird in einem 5 Stufensystem der Ladezustand des Trackers angezeigt. Jede Stufe entpricht etwa 3
Stunden und 10 Minuten (Gesamtlaufzeit etwa 16 Stunden.)
##### 2.1.5 GPS-Status
Hier wird dargestellt ob der Tracker bei seinem letzten Empfang ein gültiges GPS-Signal gesendet hat, also
selbst Empfang hatte. In Gebäuden oder unter Brücken kann es passieren das der Tracker keinen Empfang
mehr hat. Ebenso ist der Empfang nach dem Einsachalten nicht sofort verfügbar.
##### 2.1.6 Letzer Signalempfang
In dieser Zeile wird die Zeitspanne seit dem letzten Signalempfang über Funk vom Tracker angezeigt. Somit
ist ersichtlich wie Aktuell die genannte Posiotion auf der Karte ist.
#### 2.2 Tracker-Detail
![Trackerdetail](Trackerdetail.png)
Diese Ansicht zeigt Details zu einem Tracker an, sobald dieser über die Karte oder die Gesamtliste
ausgewählt wurde. Sollte bei einem Tracker der Alarmknopf gedrückt werden, wird diese Ansicht ebenfalls
geöffnet.
##### 2.2.1 Name
Siehe 2.1.3.
##### 2.2.2 Akkusymbol
Siehe 2.1.4. Zusätzlich wird die Spannung in Volt angezeigt.
##### 2.2.3 GPS-Status
Siehe 2.1.5.
##### 2.2.4 MGRS-Koordinaten
Koordinaten des Trackers im UTM-Referenzsystem. Hier wird der zuletzt empfangene Wert angezeigt.
##### 2.2.5 Höhe
Die Höhe der Position des Trackers in Meter über NN.
##### 2.2.6 HDOP
Die [ungenauigkeit](https://de.wikipedia.org/wiki/Dilution_of_Precision) des GPS-Signals. Achtung, dieser
Wert ist keine Angabe in Metern. Gute Werte sind kleiner 1,2
##### 2.2.7 GPS-Koordinaten
Die MGRS-Koordinaten umgerechnet in Dezimalgraden.
##### 2.2.8 Letzter GPS Wert
Diese Angabe bezieht sich auf die letzten korrekt Empfangenen Koordinaten, somit kann festgestellt werden
ob ein Tracker GPS-Empfang hat. Ein Tracker kann unter umständen Empfangen werden, aber z.B. innerhalb
eines Gebäudes sein und selbst keinen GPS-Empfang haben. Dort wird dann angezeigt wann er selbst zuletzt
Empfang hatte.
##### 2.2.9 Update
Zeitpunkt und Zeitraum des zuletzt empfangen Funksignals.
##### 2.2.10 Empfangstärke
RSSI (Received signal strength indication) und SNR (Signal-to-noise ratio) geben die Qualität der
Funkverbindung zum Tracker an. Je größer die Werte desto besser.
##### 2.2.11 Alerts
Hier werden die letzten 10 Zeitpunkte der Alarmmeldungen angezeigt.
### 3. Filter
![Filtersymbol](Symbol-Filter.png)
Über diesen Schalter lassen sich die Filtereinstellungen aufrufen.
### 3.1 Filter-Auswahl
![Filterauswahl](Filter-Select.png)
In der Liste können die Gruppen ausgewählt werden, welche auf der Karte dargestellt werden sollen. Es
können mehrere Gruppen gleichzeit oder auch garkeine ausgewält werden (dann werden alle dargestellt).
### 3.2 Filter-Details

View File

@ -1,39 +0,0 @@
# Lora-Map
Lora-Map ist eine Karte die GPS-Positionen von Geräten anzeigt.
![Global mit Anmerkungen](Global.png)
## Bedienung
In diesem abschnitt findet sich eine Bedienungsnleitung über alle Funktionen der Karte.
### 1. Zoombuttons
![Zoombuttons](Zoombuttons.png)
Diese Knöpfe werden zum Steuern der Karte verwendet. Dabei kann mit `+` in die Karte hineingezoomt werden und mit `-` aus der Karte herraus.
### 2. Tracker
![Trackerbutton](Tracker.png)
Über diesen Schalter lassen sich Informationen über alle, sowie über einzelne Tracker abrufen.
#### 2.1 Gesamtliste
![Gesamtliste](Gesamtliste.png)
Hier wird eine Übersicht über alle aktiven Tracker angezeigt. Diese können angeklickt werden um Details zu diesen zu sehen [(siehe Abschnitt 2.2)][id_2_2].
##### 2.1.1 Batteriestatus
Der horizontale Balken in `Grün`, `Gelb` oder `Rot` zeigt den Akkuzustand schnell erkenntlich an. Grüne Tracker sind voll, Gelbe etwa halbvoll und Rote sollen geladen werden.
##### 2.1.2 Icon
Hier wird das Icon des Trackers dargestellt.
##### 2.1.3 Name
Hier erscheint der Eingetragene `Name` des Trackers. Dies kann ein Funkrufname oder auch ein anderes Kürzel sein. Die Anzahl der Zeichen ist begrenzt, da dieser Name auch auf der Karte über den Symbolen erscheint.
##### 2.1.4 Akkusymbol
Hier wird in einem 5 Stufensystem der Ladezustand des Trackers angezeigt. Jede Stufe entpricht etwa 3 Stunden und 10 Minuten (Gesamtlaufzeit etwa 16 Stunden.)
##### 2.1.5 GPS-Status
Hier wird dargestellt ob der Tracker bei seinem letzten Empfang ein gültiges GPS-Signal gesendet hat, also selbst Empfang hatte. In Gebäuden oder unter Brücken kann es passieren das der Tracker keinen Empfang mehr hat.
Ebenso ist der Empfang nach dem Einsachalten nicht sofort verfügbar.
##### 2.1.6 Letzer Signalempfang
In dieser Zeile wird die Zeitspanne seit dem letzten Signalempfang über Funk vom Tracker angezeigt. Somit ist ersichtlich wie Aktuell die genannte Posiotion auf der Karte ist.
#### 2.2 Tracker-Detail

BIN
doc/Symbol-Filter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
doc/Trackerdetail.pdn Normal file

Binary file not shown.

BIN
doc/Trackerdetail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

312
map-swagger.yml Normal file
View File

@ -0,0 +1,312 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Lora-Map
description: Swagger-File for the API of the Lora-Map
contact:
name: Philip Schell
email: philip.schell@fit.fraunhofer.de
license:
name: LGPL 3
url: https://www.gnu.org/licenses/lgpl-3.0.html
servers:
- url: http://localhost:8080
paths:
/api/json/camera:
get:
description: Get related data to Cameras
tags:
- api
responses:
200:
description: ok
content:
application/json:
example:
Counter:
Haupteingang:
Lastcameradata: "04/08/2021 00:20:31"
Name: "Camera 1"
Total: 50
Incoming: 35
Outgoing: 15
Density:
Hauptflaeche:
DensityCount: 10
TimeStamp: "04/08/2021 00:20:31"
AverageFlowMagnitude: 5.2
AverageFlowDirection: 175.2
LastUpdate: "04/08/2021 00:20:31"
Fights:
Kneipe:
LastUpdate: "04/08/2021 00:20:31"
TimeStamp: "04/08/2021 00:20:31"
Situation: "fight"
FightProbability: 0.7
/api/json/position:
get:
description: Get related data to Positions
tags:
- api
responses:
200:
description: ok
content:
application/json:
example:
Positions:
AA:
Rssi: 5.2
Snr: 3.2
Lorarecievedtime: "04/08/2021 00:20:31"
Recievedtime: "04/08/2021 00:20:31"
Latitude: 50.2
Longitude: 7.8
UTM:
MGRS: "32U MA 14357 61557"
Base: "32U MA"
FieldWidth: "14"
FieldHeight: "61"
Width: "357"
Height: "557"
Hdop: 1.2
Lastgpspostime: "04/08/2021 00:20:31"
Battery: 4.2
Batterysimple: 4
Fix: true
Height: 60.2
Name: "AA"
Icon: null
MenuIcon: null
Group: null
Alarms:
AA:
Rssi: 5.2
Snr: 3.2
Lorarecievedtime: "04/08/2021 00:20:31"
Recievedtime: "04/08/2021 00:20:31"
Latitude: 50.2
Longitude: 7.8
UTM:
MGRS: "32U MA 14357 61557"
Base: "32U MA"
FieldWidth: "14"
FieldHeight: "61"
Width: "357"
Height: "557"
Hdop: 1.2
Lastgpspostime: "04/08/2021 00:20:31"
Battery: 4.2
Batterysimple: 4
Fix: true
Height: 60.2
Name: "AA"
Icon: null
MenuIcon: null
Group: null
ButtonPressed:
- "04/08/2021 00:20:31"
/api/json/sensor:
get:
description: Get related data to Sensors
tags:
- api
responses:
200:
description: ok
content:
application/json:
example:
Enviroments:
Sensor1:
Name: "Sensor1"
Rssi: 50.2
Snr: 11.2
Temperature: 23.4
Humidity: 43.2
Windspeed: 12.5
Lorarecievedtime: "04/08/2021 00:20:31"
Weather:
Warnungen:
- Body: "Es tritt leichter Frost zwischen -2 °C und -5 °C auf. Vor allem bei Aufklaren über Schnee sinken die Temperaturen auf Werte bis -9 °C."
From: "2021-04-08T13:54:00Z"
Headline: "Amtliche WARNUNG vor FROST"
Id: "Warnungen_Gemeinden.808111000.2.49.0.1.276.0.DWD.PVW.1617890040000.b2a1b3ea-db16-4992-920b-ea9c1b1a905f.DEU"
Instructions: ""
Level: "minor"
Location: "Stadt Stuttgart"
To: "2021-04-09T07:00:00Z"
Type: "frost"
/api/json/settings:
get:
description: Get related data to Settings
tags:
- api
responses:
200:
description: ok
content:
application/json:
example:
Startloclat: 50.2
Startloclon: 7.2
Grid:
Major:
- from:
- 50.2
- 7.2
to:
- 50.2
- 7.1
Minor:
- from:
- 50.1
- 7.2
- to:
- 50.1
- 7.1
FightDedection:
fight:
Alias: "fightcam"
Level: 0.7
Polygon:
- - 50.2
- 7.2
- - 50.1
- 7.2
- - 50.2
- 7.1
DensityArea:
dens:
Alias: "denscam"
Maximum:
Polygon:
- - 50.2
- 7.2
- - 50.1
- 7.2
- - 50.2
- 7.1
Sensors:
temp:
Coordinates:
- 50.2
- 7.2
Level: 20
Alias: "Temperatur"
Layers:
online:
title: Online Map
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
minZoom: 1
maxZoom: 19
GeoLayer: {}
/api/time:
get:
description: Get the current server time
tags:
- api
responses:
200:
description: ok
content:
application/json:
example:
utc: "04/09/2021 21:55:37"
/api/svg/marker.svg:
get:
description: Get a complete marker for the map
tags:
- marker
responses:
200:
description: ok
/api/svg/person.svg:
get:
description: Get an person icon
tags:
- marker
responses:
200:
description: ok
/admin/login:
post:
description: Login into the Admin pannel, is needed for every other /admin reqeust. Returns a session cookie
tags:
- admin
responses:
307:
description: Redirect to /admin if login was successful and to /admin/login.html if not.
/admin/api/json/name:
get:
description: Get the raw content of names.json
tags:
- admin
responses:
200:
description: Returns the raw content
403:
description: You are not logged in, see /admin/login for infos.
put:
description: Save the raw content to names.json
tags:
- admin
responses:
200:
description: Ok if valid json in the request
403:
description: You are not logged in, see /admin/login for infos.
/admin/api/json/geo:
get:
description: Get the raw content of geo.json
tags:
- admin
responses:
200:
description: Returns the raw content
403:
description: You are not logged in, see /admin/login for infos.
put:
description: Save the raw content to geo.json
tags:
- admin
responses:
200:
description: Ok if valid json in the request
403:
description: You are not logged in, see /admin/login for infos.
/admin/api/json/setting:
get:
description: Get the raw content of settings.json
tags:
- admin
responses:
200:
description: Returns the raw content
403:
description: You are not logged in, see /admin/login for infos.
put:
description: Save the raw content to settings.json
tags:
- admin
responses:
200:
description: Ok if valid json in the request
403:
description: You are not logged in, see /admin/login for infos.