From e388eb6626f62ebe0169d80253905b902dd61d35 Mon Sep 17 00:00:00 2001
From: Philip Schell <philip.schell@fit.fraunhofer.de>
Date: Wed, 21 Aug 2019 13:19:02 +0200
Subject: [PATCH] #28 Fightdedection Plygon on Map

---
 CHANGELOG                          |   1 +
 Lora-Map/Lora-Map.csproj           |   1 +
 Lora-Map/Model/Crowd.cs            | 114 +++++++++++++----------------
 Lora-Map/Model/Fight.cs            |  35 +++++++++
 Lora-Map/Model/Settings.cs         |  19 +++++
 Lora-Map/Server.cs                 |  13 +++-
 Lora-Map/resources/js/functions.js |   1 +
 Lora-Map/resources/js/map.js       |  28 +++++++
 8 files changed, 148 insertions(+), 64 deletions(-)
 create mode 100644 Lora-Map/Model/Fight.cs

diff --git a/CHANGELOG b/CHANGELOG
index f11818b..1811fcf 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,7 @@
 * Add setting model to code
 * #19 grid automatisch generieren
 * #24 Add information about weather/warning
+* #28 Fightdedection Plygon on Map
 ### Bugfixes
 * Add Correct Dispose Handling
 * TimeCalculation now handle negative values correct
diff --git a/Lora-Map/Lora-Map.csproj b/Lora-Map/Lora-Map.csproj
index 80367c7..956e965 100644
--- a/Lora-Map/Lora-Map.csproj
+++ b/Lora-Map/Lora-Map.csproj
@@ -50,6 +50,7 @@
     <Compile Include="Model\Admin\AdminSession.cs" />
     <Compile Include="Model\Camera.cs" />
     <Compile Include="Model\Crowd.cs" />
+    <Compile Include="Model\Fight.cs" />
     <Compile Include="Model\Marker.cs" />
     <Compile Include="Model\AlarmItem.cs" />
     <Compile Include="Model\Settings.cs" />
diff --git a/Lora-Map/Model/Crowd.cs b/Lora-Map/Model/Crowd.cs
index fcf8a19..c66dd9c 100644
--- a/Lora-Map/Model/Crowd.cs
+++ b/Lora-Map/Model/Crowd.cs
@@ -1,63 +1,51 @@
-using LitJson;
-using System;
-using System.Globalization;
-
-namespace Fraunhofer.Fit.IoT.LoraMap.Model
-{
-  public class Crowd
-  {
-    public Int32 DensityCount { get; private set; }
-    public DateTime LastUpdate { get; private set; }
-    public Double AverageFlowMagnitude { get; private set; }
-    public Double AverageFlowDirection { get; private set; }
-    public Double Cofidence { get; private set; }
-    public String Situation { get; private set; }
-
-    public Crowd(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 &&
-      json.ContainsKey("type_module") && json["type_module"].IsString && json["type_module"].ToString() == "crowd_density_local" &&
-      json.ContainsKey("density_count") && json["density_count"].IsInt &&
-      json.ContainsKey("timestamp1") && json["timestamp1"].IsString;
-
-    public static Boolean CheckJsonFlow(JsonData json) => json.ContainsKey("camera_ids") && json["camera_ids"].IsArray && json["camera_ids"].Count == 1 &&
-      json.ContainsKey("average_flow_magnitude") && json["average_flow_magnitude"].IsArray &&
-      json.ContainsKey("type_module") && json["type_module"].IsString && json["type_module"].ToString() == "flow" &&
-      json.ContainsKey("average_flow_direction") && json["average_flow_direction"].IsArray &&
-      json.ContainsKey("timestamp") && json["timestamp"].IsString;
-
-    public static Boolean CheckJsonFightingDetection(JsonData json) => json.ContainsKey("camera_ids") && json["camera_ids"].IsArray && json["camera_ids"].Count == 1 &&
-      json.ContainsKey("confidence") && json["confidence"].IsDouble &&
-      json.ContainsKey("type_module") && json["type_module"].IsString && json["type_module"].ToString() == "fighting_detection" &&
-      json.ContainsKey("situation") && json["situation"].IsString &&
-      json.ContainsKey("timestamp") && json["timestamp"].IsString;
-
-    public static String GetId(JsonData json) => (String)json["camera_ids"][0];
-
-    public void Update(JsonData json) {
-      if(CheckJsonCrowdDensityLocal(json)) {
-        this.DensityCount = (Int32)json["density_count"];
-        if (DateTime.TryParse((String)json["timestamp1"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
-          this.LastUpdate = updatetime.ToUniversalTime();
-        }
-      } else if(CheckJsonFlow(json)) {
-        if (json["average_flow_magnitude"].Count == 1) {
-          this.AverageFlowMagnitude = (Double)json["average_flow_magnitude"][0];
-        }
-        if (json["average_flow_direction"].Count == 1) {
-          this.AverageFlowDirection = (Double)json["average_flow_direction"][0];
-        }
-        if (DateTime.TryParse((String)json["timestamp"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
-          this.LastUpdate = updatetime.ToUniversalTime();
-        }
-      } else if(CheckJsonFightingDetection(json)) {
-        this.Cofidence = (Double)json["confidence"];
-        this.Situation = (String)json["situation"];
-        if (DateTime.TryParse((String)json["timestamp"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
-          this.LastUpdate = updatetime.ToUniversalTime();
-        }
-      }
-    }
-  }
-}
+using LitJson;
+using System;
+using System.Globalization;
+
+namespace Fraunhofer.Fit.IoT.LoraMap.Model
+{
+  public class Crowd
+  {
+    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 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 &&
+      json.ContainsKey("type_module") && json["type_module"].IsString && json["type_module"].ToString() == "crowd_density_local" &&
+      json.ContainsKey("density_count") && json["density_count"].IsInt &&
+      json.ContainsKey("timestamp1") && json["timestamp1"].IsString;
+
+    public static Boolean CheckJsonFlow(JsonData json) => json.ContainsKey("camera_ids") && json["camera_ids"].IsArray && json["camera_ids"].Count == 1 &&
+      json.ContainsKey("average_flow_magnitude") && json["average_flow_magnitude"].IsArray &&
+      json.ContainsKey("type_module") && json["type_module"].IsString && json["type_module"].ToString() == "flow" &&
+      json.ContainsKey("average_flow_direction") && json["average_flow_direction"].IsArray &&
+      json.ContainsKey("timestamp") && json["timestamp"].IsString;
+
+    public static String GetId(JsonData json) => (String)json["camera_ids"][0];
+
+    public void Update(JsonData json) {
+      if(CheckJsonCrowdDensityLocal(json)) {
+        this.DensityCount = (Int32)json["density_count"];
+        if (DateTime.TryParse((String)json["timestamp1"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
+          this.TimeStamp = updatetime.ToUniversalTime();
+        }
+      } else if(CheckJsonFlow(json)) {
+        if (json["average_flow_magnitude"].Count == 1) {
+          this.AverageFlowMagnitude = (Double)json["average_flow_magnitude"][0];
+        }
+        if (json["average_flow_direction"].Count == 1) {
+          this.AverageFlowDirection = (Double)json["average_flow_direction"][0];
+        }
+        if (DateTime.TryParse((String)json["timestamp"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
+          this.TimeStamp = updatetime.ToUniversalTime();
+        }
+      } 
+      this.LastUpdate = DateTime.UtcNow;
+    }
+  }
+}
diff --git a/Lora-Map/Model/Fight.cs b/Lora-Map/Model/Fight.cs
new file mode 100644
index 0000000..d937cdb
--- /dev/null
+++ b/Lora-Map/Model/Fight.cs
@@ -0,0 +1,35 @@
+using LitJson;
+using System;
+using System.Globalization;
+
+namespace Fraunhofer.Fit.IoT.LoraMap.Model {
+  public class Fight {
+    public Fight(JsonData json) => this.Update(json);
+
+    public DateTime LastUpdate { get; private set; }
+    public DateTime TimeStamp { get; private set; }
+    public String Situation { get; private set; }
+    public Double FightProbability { get; private set; }
+
+    public static Boolean CheckJsonFightingDetection(JsonData json) => json.ContainsKey("camera_ids") && json["camera_ids"].IsArray && json["camera_ids"].Count == 1 &&
+      json.ContainsKey("confidence") && json["confidence"].IsString &&
+      json.ContainsKey("type_module") && json["type_module"].IsString && json["type_module"].ToString() == "fighting_detection" &&
+      json.ContainsKey("situation") && json["situation"].IsString &&
+      json.ContainsKey("timestamp") && json["timestamp"].IsString;
+
+    public static String GetId(JsonData json) => (String)json["camera_ids"][0];
+
+    public void Update(JsonData json) {
+      if (CheckJsonFightingDetection(json)) {
+        if (Double.TryParse((String)json["confidence"], NumberStyles.Float, CultureInfo.InvariantCulture, out Double cofidence)) {
+          this.FightProbability = cofidence;
+        }
+        this.Situation = (String)json["situation"];
+        if (DateTime.TryParse((String)json["timestamp"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
+          this.TimeStamp = updatetime.ToUniversalTime();
+        }
+        this.LastUpdate = DateTime.UtcNow;
+      }
+    }
+  }
+}
diff --git a/Lora-Map/Model/Settings.cs b/Lora-Map/Model/Settings.cs
index 748666a..85c7768 100644
--- a/Lora-Map/Model/Settings.cs
+++ b/Lora-Map/Model/Settings.cs
@@ -12,6 +12,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
     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, List<List<Double>>> FightDedection { get; private set; }
 
     public Settings() => this.ParseJson();
     
@@ -36,6 +37,24 @@ namespace Fraunhofer.Fit.IoT.LoraMap.Model {
           }
         }
       }
+      if(json.ContainsKey("FightDedection") && json["FightDedection"].IsObject) {
+        Dictionary<String, List<List<Double>>> fight = new Dictionary<String, List<List<Double>>>();
+        foreach (KeyValuePair<String, JsonData> entry in json["FightDedection"]) {
+          List<List<Double>> poly = new List<List<Double>>();
+          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) {
+                coords.Add((Double)coord["Lat"]);
+                coords.Add((Double)coord["Lon"]);
+              }
+              poly.Add(coords);
+            }
+          }
+          fight.Add(entry.Key, poly);
+        }
+        this.FightDedection = fight;
+      }
       this.gridradius = json.ContainsKey("GridRadius") && json["GridRadius"].IsInt && this.Startloclat != 0 && this.Startloclon != 0 ? (Int32)json["GridRadius"] : 0;
       this.GenerateGrid();
     }
diff --git a/Lora-Map/Server.cs b/Lora-Map/Server.cs
index 33be813..1374279 100644
--- a/Lora-Map/Server.cs
+++ b/Lora-Map/Server.cs
@@ -17,6 +17,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
     private readonly SortedDictionary<String, AlarmItem> alarms = new SortedDictionary<String, AlarmItem>();
     private readonly SortedDictionary<String, Camera> cameras = new SortedDictionary<String, Camera>();
     private readonly SortedDictionary<String, Crowd> crowds = new SortedDictionary<String, Crowd>();
+    private readonly SortedDictionary<String, Fight> fights = new SortedDictionary<String, Fight>();
     private JsonData marker;
     private readonly Settings settings;
     private readonly WeatherWarnings weather;
@@ -24,6 +25,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
     private readonly AdminModel admin;
     private readonly Object lockData = new Object();
     private readonly Object lockPanic = new Object();
+    private readonly Object lockFight = new Object();
 
     public Server(ADataBackend backend, Dictionary<String, String> settings, InIReader requests) : base(backend, settings, requests) {
       this.logger.SetPath(settings["loggingpath"]);
@@ -100,7 +102,6 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
             this.cameras.Add(cameraid, new Camera(d));
           }
         } else if((((String)mqtt.From).Contains("sfn/crowd_density_local") && Crowd.CheckJsonCrowdDensityLocal(d)) || 
-          (((String)mqtt.From).Contains("sfn/fighting_detection") && Crowd.CheckJsonFightingDetection(d)) || 
           (((String)mqtt.From).Contains("sfn/flow") && Crowd.CheckJsonFlow(d))) {
           String cameraid = Crowd.GetId(d);
           if(this.crowds.ContainsKey(cameraid)) {
@@ -108,6 +109,15 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
           } else {
             this.crowds.Add(cameraid, new Crowd(d));
           }
+        } else if(((String)mqtt.From).Contains("camera/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));
+            }
+          }
         }
       } catch(Exception e) {
         Helper.WriteError("Backend_MessageIncomming(): "+e.Message + "\n\n" + e.StackTrace);
@@ -122,6 +132,7 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
             { "panic", this.alarms },
             { "cameracount", this.cameras },
             { "crowdcount", this.crowds },
+            { "fightdedect", this.fights },
             { "weatherwarnings", this.weather.Warnungen }
           }, cont);
         } else if (cont.Request.Url.PathAndQuery.StartsWith("/get60000")) {
diff --git a/Lora-Map/resources/js/functions.js b/Lora-Map/resources/js/functions.js
index f4b2652..73d4c5b 100644
--- a/Lora-Map/resources/js/functions.js
+++ b/Lora-Map/resources/js/functions.js
@@ -29,6 +29,7 @@
         OverlayObject._ParseAJAXCount(json["cameracount"]);
         OverlayObject._ParseAJAXDensity(json["crowdcount"]);
         MenuObject._ParseAJAXWeatherAlerts(json["weatherwarnings"]);
+        MapObject._ParseAJAXFightDedection(json["fightdedect"]);
       }
     };
     get1000.open("GET", "/get1000", true);
diff --git a/Lora-Map/resources/js/map.js b/Lora-Map/resources/js/map.js
index 1e73f06..d4d9da9 100644
--- a/Lora-Map/resources/js/map.js
+++ b/Lora-Map/resources/js/map.js
@@ -1,5 +1,6 @@
 var MapObject = {
   Map: {},
+  _FightDedection: {},
   _SpecialMarkers: new Array(),
   Start: function () {
     this.Map = L.map('bigmap').setView([0, 0], 16);
@@ -10,6 +11,7 @@
   _ParseAJAXSettings: function (settings) {
     this.Map.panTo([settings.Startloclat, settings.Startloclon]);
     this._GenerateGrid(settings.Grid);
+    this._GenerateFightBoxes(settings.FightDedection);
   },
   _ParseAJAXLayers: function (maps) {
     var i = 0;
@@ -60,6 +62,32 @@
       L.polyline([[lineminor.from[0], lineminor.from[1]], [lineminor.to[0], lineminor.to[1]]], { color: "red", weight: 0.7, opacity: 0.5 }).addTo(this.Map);
     }
   },
+  _GenerateFightBoxes: function (fightdedection) {
+    for (var cameraid in fightdedection) {
+      this._FightDedection[cameraid] = L.polygon(fightdedection[cameraid], { color: 'black', weight: 1 }).addTo(this.Map);
+      this._FightDedection[cameraid].bindPopup("Fightdedection für Kamera: " + cameraid);
+    }
+  },
+  _ParseAJAXFightDedection: function (json) {
+    for (var cameraid in json) {
+      if (this._FightDedection.hasOwnProperty(cameraid)) {
+        var fight = json[cameraid];
+        var box = this._FightDedection[cameraid];
+        var diff = FunctionsObject.TimeCalculation(fight["LastUpdate"], "diffraw");
+        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)) {
       L.geoJSON(geo, {