Compare commits

..

66 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
Philip Schell
61ed34899d
[1.3.0] New Gateway
## New Features
* Implement Lora-Gateway-Data 1.1.0 Format
## Bugfixes
## Changes
* Refactoring
* Make requests.conf not needed anymore.
2020-01-20 14:39:51 +01:00
c96941b048 [1.3.0] New Gateway 2020-01-20 14:37:06 +01:00
c4c8f6164b Fix tiny warning 2020-01-19 23:16:16 +01:00
0a60a82b8c Refactoring Alarmitem, becuase its only a bit different from PositionItem, so use oop 2020-01-19 23:08:43 +01:00
Philip Schell
88003a17bb
Merge pull request #30 from MONICA-Project/v1.3.1
Using wront branch
2020-01-19 16:31:52 +01:00
65c5f500d1 Make PositionItem ready Lora-Gateway-Data 1.1.0 data 2020-01-18 11:49:58 +01:00
Philip Schell
977e5415fd
Merge pull request #29 from MONICA-Project/v1.2.10
Create Release v1.2.10
2019-12-11 11:55:29 +01:00
655e010a53 [1.2.10] Refactoring is the thing 2019-12-11 11:51:37 +01:00
479a3b72d4 Move to NET .Core 2019-12-10 15:17:58 +01:00
bef2f6d129 Add netcore 2019-12-09 15:05:52 +01:00
88344a64c4 Tiny fix in fontcolor 2019-11-24 16:32:51 +01:00
1e0690fed1 Update Sensor 2019-11-21 14:36:06 +01:00
da34f0566d Start add Feature Sensors on map 2019-11-20 20:04:23 +01:00
ec2500d72f Rename and add root files 2019-11-06 19:35:27 +01:00
07045a8450 Rename 2019-10-29 18:07:17 +01:00
1082a85ad5 renaming folder 2019-10-29 17:56:44 +01:00
f8cd3b4037 Reordering 2019-10-29 17:55:26 +01:00
8e93703e2a new README.md 2019-10-29 11:46:52 +01:00
17c20a9b69 Doku weiter 2019-10-28 15:33:16 +01:00
0739e18714 Layer und Wetter eingefügt 2019-10-07 09:30:58 +02:00
71b43d1c9d README.md weitergearbeitet 2019-10-04 19:01:25 +02:00
8db03227fe Mehr Doku 2019-10-04 18:20:09 +02:00
4cca4351bc Zugeschnitten 2019-10-04 13:49:32 +02:00
f43971e8be Update Communication Docu 2019-09-25 13:43:08 +02:00
986a122333 Parse also all Doublevalues as Int
Start Docu
2019-09-24 15:13:36 +02:00
377bdfe9e0 Screenshots for Doku 2019-09-20 14:32:59 +02:00
521bb6c4c6 popup box max width bigger 2019-09-07 17:20:56 +02:00
6b1fca9109 Create Aliases for Camera Count
Filter Fight under level
2019-09-07 16:09:23 +02:00
286f931e59 Display GateCounting Boxes in a line not a collumn 2019-09-06 19:19:04 +02:00
7641cb50eb Change Sani to Rettungsdienst 2019-09-06 19:18:06 +02:00
fa02c89674 Search in Description of Polygons 2019-09-06 19:17:34 +02:00
7c7e15e1b2 Numbers on places are not clickable, also as the grid 2019-09-03 23:26:16 +02:00
617eef2f5f Show Numbers from a Place not with linebreaking 2019-09-03 23:04:45 +02:00
cec861cd7c Searchfunction case insensitive 2019-09-02 16:03:49 +02:00
69e1ff73b0 Correct Changelog please 2019-09-02 08:55:18 +02:00
df0593ee38 Change Online Map MaxZoom to 20 2019-08-31 22:55:55 +02:00
f192cc7b52 Merge branch 'v1.2.9' into 'master'
[1.2.9] The PüMa Release

See merge request monica/lora-map/lora-map!2
2019-08-30 15:53:51 +02:00
10f9e32678 Prepare for next release 2019-08-30 15:34:16 +02:00
1f3d864249 #15 suche nach ständen einbauen 2019-08-30 15:04:51 +02:00
1d0d64d314 #16 filter nach kategorien/tracker einbauen 2019-08-29 21:12:08 +02:00
de28446c0d bf 2019-08-28 13:11:05 +02:00
968f6f411b Tiny Weather fixes 2019-08-28 12:51:16 +02:00
3fda36ecbc #16 Start of Frontend 2019-08-25 19:15:46 +02:00
98bd2cafe9 #16 Implement Backend for Settings 2019-08-25 14:19:48 +02:00
26bbf9c6c5 Start of #16 2019-08-23 14:09:52 +02:00
28bf1c585b #27 Draw Camera-Desity bock on map 2019-08-22 13:45:11 +02:00
438584f4b9 Begin of #27, Implement Settings 2019-08-21 19:12:26 +02:00
e388eb6626 #28 Fightdedection Plygon on Map 2019-08-21 13:19:02 +02:00
84162714fe #28 Settings now editable 2019-08-20 20:03:53 +02:00
680b4b5d82 Start working on #28 2019-08-20 15:24:34 +02:00
9f9c8e63ef #24 Add information about weather/warning 2019-08-20 13:08:56 +02:00
c4a23751c1 #24 Implement Backendstuff 2019-08-19 19:57:42 +02:00
f65b81b369 Add Correct Dispose Handling 2019-08-02 19:04:51 +02:00
36a6b86a5b Start of WeatherWarnings 2019-08-01 17:29:16 +02:00
b1832da477 #19 grid automatisch generieren 2019-07-30 21:17:56 +02:00
a2b3f2c5da Add setting model to code
#19 grid automatisch generieren
2019-07-30 15:34:45 +02:00
d0a4430d0e add settings.json 2019-07-29 14:42:42 +02:00
82d51c5dc1 Refactoring Adminpannel 2019-07-26 11:48:30 +02:00
123411eca6 Refactoring Js finished
Make only one request per second instead of four per AJAX
2019-07-25 11:14:04 +02:00
22c37456e9 Move querys together 2019-07-17 15:55:01 +02:00
f4efe2ab66 Refactoring of all JS finished. 2019-07-17 14:23:06 +02:00
b214aaecee Javascript Refactoring... 2019-07-16 15:42:49 +02:00
88 changed files with 6353 additions and 1940 deletions

106
.github/workflows/dotnetcore.yml vendored Normal file
View File

@ -0,0 +1,106 @@
name: Build, Test, Publish and Dockerise Lora-Map
on: [push]
jobs:
build:
name: Build, pack and release
runs-on: ubuntu-latest
steps:
- name: Checkout parent project with dependencys
uses: actions/checkout@v1
with:
repository: MONICA-Project/map-project
ref: refs/heads/master
submodules: true
- name: Checkout last versions
run: git -C Lora-Map checkout --progress --force ${{ github.sha }}
working-directory: ../map-project
- name: Install dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.x'
- name: Build with dotnet
run: dotnet build Lora-Map.sln --configuration Release
working-directory: ../map-project/Lora-Map
- name: Create deb files
if: success()
run: |
mkdir ../../../Builds
chmod oug+x make-deb.sh
./make-deb.sh amd64
./make-deb.sh armhf
id: create_deb
working-directory: ../map-project/Lora-Map/Lora-Map/dpkg
- name: Create release
if: success()
id: nightly_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.create_deb.outputs.builddaterelease }}
release_name: Nightly from ${{ steps.create_deb.outputs.builddaterelease }}
body: This is a nightly release. It may be not working properly.
draft: false
prerelease: true
- name: Upload release asset amd64
if: success()
id: upload-release-asset-amd64
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.nightly_release.outputs.upload_url }}
asset_path: ../map-project/Builds/amd64-${{ steps.create_deb.outputs.debuilderfile }}
asset_name: amd64-${{ steps.create_deb.outputs.debuilderfile }}
asset_content_type: application/x-deb
- name: Upload release asset armhf
if: success()
id: upload-release-asset-armhf
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.nightly_release.outputs.upload_url }}
asset_path: ../map-project/Builds/armhf-${{ steps.create_deb.outputs.debuilderfile }}
asset_name: armhf-${{ steps.create_deb.outputs.debuilderfile }}
asset_content_type: application/x-deb
docker:
name: Build and push dockerfile
runs-on: ubuntu-latest
steps:
- name: Checkout parent project with dependencys
uses: actions/checkout@v1
with:
repository: MONICA-Project/map-project
ref: refs/heads/master
submodules: true
- name: Checkout last versions
run: git -C Lora-Map checkout --progress --force ${{ github.sha }}
working-directory: ../map-project
- name: Docker build
id: docker_build
run: |
DOCKERTAG=$(date +%Y%m%d%H%M%S)
echo "##[set-output name=dockertag;]$DOCKERTAG"
docker build . -t monicaproject/lora-map:latest -t monicaproject/lora-map:$DOCKERTAG
working-directory: ../map-project
- name: Docker-compose publish
if: success()
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin ${INPUT_REGISTRY}
docker push monicaproject/lora-map:latest
docker push monicaproject/lora-map:${{ steps.docker_build.outputs.dockertag }}
working-directory: ../map-project

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/.vs
/Lora-Map/obj
/Lora-Map/bin
/Doku/.vs

View File

@ -1,5 +1,49 @@
# Changelogs
## 1.3.0 - New Gateway
### New Features
* Implement Lora-Gateway-Data 1.1.0 Format
### Bugfixes
### Changes
* Refactoring
* Make requests.conf not needed anymore.
## 1.2.10
### New Features
* Posibility to display sensor data on map
### Bugfixes
* Parse also all Doublevalues as Int
### Changes
* Change Online Map MaxZoom to 19
* Searchfunction case insensitive
* Show Numbers from a Place not with linebreaking
* Numbers on places are not clickable, also as the grid
* Search in Description of Polygons
* Change Sani to Rettungsdienst
* Display GateCounting Boxes in a line not a collumn
* Create Aliases for Camera Count
* Filter Fight under level
* Refactoring
* Porting to .NET Core
## 1.2.9
### New Features
* Add setting model to code
* #19 grid automatisch generieren
* #24 Add information about weather/warning
* #28 Fightdedection Plygon on Map
* #27 Draw Camera-Desity bock on map
* #16 filter nach kategorien/tracker einbauen
* #15 suche nach ständen einbauen
### Bugfixes
* Add Correct Dispose Handling
* TimeCalculation now handle negative values correct
### Changes
* Refactoring of all JS
* Make only one request per second instead of four per AJAX
* Refactoring adminpannel and add settings.json
* New function in TimeCalculation, so you can not have negative Timespans
## 1.2.8
### New Features
* Implement #12 Make icon transparent if there is no data update

BIN
Lora-Map.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,25 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2026
# Visual Studio Version 16
VisualStudioVersion = 16.0.29519.87
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lora-Map", "Lora-Map\Lora-Map.csproj", "{95D6F48A-9488-42A6-A973-941B45B26DB8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lora-Map_Core", "Lora-Map\Lora-Map.csproj", "{78136B15-FF0B-4DCE-94CA-1D6148DEA232}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils-IoT", "..\Utils\Utils-IoT\Utils-IoT\Utils-IoT.csproj", "{B870E4D5-6806-4A0B-B233-8907EEDC5AFC}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7DD32F31-ACB0-4F5E-B3D8-78564A83ACEF}"
ProjectSection(SolutionItems) = preProject
..\.dockerignore = ..\.dockerignore
..\CONTRIBUTING.md = ..\CONTRIBUTING.md
..\docker-compose.yml = ..\docker-compose.yml
..\Dockerfile = ..\Dockerfile
..\LICENSE = ..\LICENSE
..\README.md = ..\README.md
..\update.sh = ..\update.sh
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectorDataMqtt", "..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt.csproj", "{EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot-Utils", "..\Utils\Bot-Utils\Bot-Utils\Bot-Utils.csproj", "{ED37370F-AE65-498D-A425-413FEE69C0A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "litjson_4.7.1", "..\Librarys\litjson\litjson\litjson_4.7.1.csproj", "{91A14CD2-2940-4500-8193-56D37EDDDBAA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "..\Utils\Utils\Utils\Utils.csproj", "{E8268FE5-D6F0-4805-9BDE-9DBC9CB517FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "M2Mqtt_4.7.1", "..\Librarys\mqtt\M2Mqtt\M2Mqtt_4.7.1.csproj", "{A11AEF5A-B246-4FE8-8330-06DB73CC8074}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils-IoT", "..\Utils\Utils-IoT\Utils-IoT\Utils-IoT.csproj", "{04CF6328-3976-44D3-9959-A3B1A2C5C45A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bot-Utils", "..\Utils\Bot-Utils\Bot-Utils\Bot-Utils.csproj", "{BB7BFCB5-3DB0-49E1-802A-3CE3EECC59F9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorDataMqtt", "..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt.csproj", "{E40D29CB-B499-4FA6-AEA1-18E8CEAA911B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "..\Utils\Utils\Utils\Utils.csproj", "{FAC8CE64-BF13-4ECE-8097-AEB5DD060098}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "litjson", "..\Librarys\litjson\litjson\litjson.csproj", "{FFC66B7F-B4FB-4E42-B896-2C6772D899AA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Posix", "..\Librarys\Mono.Posix\Mono.Posix\Mono.Posix.csproj", "{E2CA132E-E85C-40AD-BE94-B138AA68772B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "M2Mqtt", "..\Librarys\mqtt\M2Mqtt\M2Mqtt.csproj", "{00C678EE-6BAA-4FCB-AAA5-7755E65C6CC5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoordinateSharp", "..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp.csproj", "{DA8510CE-7899-49DD-9E17-7C974382288D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoordinateSharp", "..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp.csproj", "{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -27,47 +36,43 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{95D6F48A-9488-42A6-A973-941B45B26DB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95D6F48A-9488-42A6-A973-941B45B26DB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95D6F48A-9488-42A6-A973-941B45B26DB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95D6F48A-9488-42A6-A973-941B45B26DB8}.Release|Any CPU.Build.0 = Release|Any CPU
{B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B870E4D5-6806-4A0B-B233-8907EEDC5AFC}.Release|Any CPU.Build.0 = Release|Any CPU
{EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE6C8F68-ED46-4C1C-ABDD-CFCDF75104F2}.Release|Any CPU.Build.0 = Release|Any CPU
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91A14CD2-2940-4500-8193-56D37EDDDBAA}.Release|Any CPU.Build.0 = Release|Any CPU
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A11AEF5A-B246-4FE8-8330-06DB73CC8074}.Release|Any CPU.Build.0 = Release|Any CPU
{BB7BFCB5-3DB0-49E1-802A-3CE3EECC59F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB7BFCB5-3DB0-49E1-802A-3CE3EECC59F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB7BFCB5-3DB0-49E1-802A-3CE3EECC59F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB7BFCB5-3DB0-49E1-802A-3CE3EECC59F9}.Release|Any CPU.Build.0 = Release|Any CPU
{FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Release|Any CPU.Build.0 = Release|Any CPU
{E2CA132E-E85C-40AD-BE94-B138AA68772B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E2CA132E-E85C-40AD-BE94-B138AA68772B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E2CA132E-E85C-40AD-BE94-B138AA68772B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E2CA132E-E85C-40AD-BE94-B138AA68772B}.Release|Any CPU.Build.0 = Release|Any CPU
{DA8510CE-7899-49DD-9E17-7C974382288D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA8510CE-7899-49DD-9E17-7C974382288D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA8510CE-7899-49DD-9E17-7C974382288D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA8510CE-7899-49DD-9E17-7C974382288D}.Release|Any CPU.Build.0 = Release|Any CPU
{78136B15-FF0B-4DCE-94CA-1D6148DEA232}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{78136B15-FF0B-4DCE-94CA-1D6148DEA232}.Debug|Any CPU.Build.0 = Debug|Any CPU
{78136B15-FF0B-4DCE-94CA-1D6148DEA232}.Release|Any CPU.ActiveCfg = Release|Any CPU
{78136B15-FF0B-4DCE-94CA-1D6148DEA232}.Release|Any CPU.Build.0 = Release|Any CPU
{ED37370F-AE65-498D-A425-413FEE69C0A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED37370F-AE65-498D-A425-413FEE69C0A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED37370F-AE65-498D-A425-413FEE69C0A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED37370F-AE65-498D-A425-413FEE69C0A8}.Release|Any CPU.Build.0 = Release|Any CPU
{E8268FE5-D6F0-4805-9BDE-9DBC9CB517FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8268FE5-D6F0-4805-9BDE-9DBC9CB517FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8268FE5-D6F0-4805-9BDE-9DBC9CB517FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8268FE5-D6F0-4805-9BDE-9DBC9CB517FF}.Release|Any CPU.Build.0 = Release|Any CPU
{04CF6328-3976-44D3-9959-A3B1A2C5C45A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04CF6328-3976-44D3-9959-A3B1A2C5C45A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04CF6328-3976-44D3-9959-A3B1A2C5C45A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04CF6328-3976-44D3-9959-A3B1A2C5C45A}.Release|Any CPU.Build.0 = Release|Any CPU
{E40D29CB-B499-4FA6-AEA1-18E8CEAA911B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E40D29CB-B499-4FA6-AEA1-18E8CEAA911B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E40D29CB-B499-4FA6-AEA1-18E8CEAA911B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E40D29CB-B499-4FA6-AEA1-18E8CEAA911B}.Release|Any CPU.Build.0 = Release|Any CPU
{FFC66B7F-B4FB-4E42-B896-2C6772D899AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FFC66B7F-B4FB-4E42-B896-2C6772D899AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFC66B7F-B4FB-4E42-B896-2C6772D899AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFC66B7F-B4FB-4E42-B896-2C6772D899AA}.Release|Any CPU.Build.0 = Release|Any CPU
{00C678EE-6BAA-4FCB-AAA5-7755E65C6CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00C678EE-6BAA-4FCB-AAA5-7755E65C6CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00C678EE-6BAA-4FCB-AAA5-7755E65C6CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00C678EE-6BAA-4FCB-AAA5-7755E65C6CC5}.Release|Any CPU.Build.0 = Release|Any CPU
{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1534B636-9FC8-49BB-BB14-FC403ECDF889}
SolutionGuid = {7B1C516B-2EDC-4F6A-A1A5-A9BCA14C8603}
EndGlobalSection
EndGlobal

View File

@ -4,73 +4,8 @@
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC-Manifestoptionen
Wenn Sie die Ebene der Benutzerkontensteuerung für Windows ändern möchten, ersetzen Sie den
Knoten "requestedExecutionLevel" wie folgt.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Durch Angabe des Elements "requestedExecutionLevel" wird die Datei- und Registrierungsvirtualisierung deaktiviert.
Entfernen Sie dieses Element, wenn diese Virtualisierung aus Gründen der Abwärtskompatibilität
für die Anwendung erforderlich ist.
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Eine Liste der Windows-Versionen, unter denen diese Anwendung getestet
und für die sie entwickelt wurde. Wenn Sie die Auskommentierung der entsprechenden Elemente aufheben,
wird von Windows automatisch die kompatibelste Umgebung ausgewählt. -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- Gibt an, dass die Anwendung mit DPI-Werten kompatibel ist und von Windows nicht automatisch auf höhere
DPI-Werte skaliert wird. WPF-Anwendungen (Windows Presentation Foundation) sind automatisch mit DPI-Werten kompatibel und müssen sich nicht
anmelden. Für Windows Forms-Anwendungen für .NET Framework 4.6, die sich für diese Einstellung anmelden, muss
auch die Einstellung "'EnableWindowsFormsHighDpiAutoResizing" in der "app.config" auf "true" festgelegt werden. -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
-->
<!-- Designs für allgemeine Windows-Steuerelemente und -Dialogfelder (Windows XP und höher) aktivieren -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View File

@ -0,0 +1,62 @@
using System;
using System.IO;
using System.Net;
using BlubbFish.Utils;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Lib {
public class WebRequests {
private static readonly Object getLock = new Object();
public JsonData GetJson(String url) {
String text = null;
for(Int32 i = 0; i < 3; i++) {
try {
text = this.GetString(url);
break;
} catch(Exception e) {
Helper.WriteError(e.Message);
if(i == 2) {
throw;
}
System.Threading.Thread.Sleep(30000);
}
}
if(text == null) {
return new JsonData();
}
try {
return JsonMapper.ToObject(text);
} catch(Exception) {
return new JsonData();
}
}
private String GetString(String url, Boolean withoutput = true) {
String ret = null;
lock(getLock) {
HttpWebRequest request = WebRequest.CreateHttp(url);
request.Timeout = 10000;
//request.Headers.Add(HttpRequestHeader.Authorization, this.auth);
try {
using(HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
if(response.StatusCode == HttpStatusCode.Unauthorized) {
Console.Error.WriteLine("Benutzer oder Passwort falsch!");
throw new Exception("Benutzer oder Passwort falsch!");
}
if(withoutput) {
StreamReader reader = new StreamReader(response.GetResponseStream());
ret = reader.ReadToEnd();
}
}
} catch(Exception e) {
//Helper.WriteError("Konnte keine Verbindung zum Razzbery Server herstellen. Resource: \"" + this.server + v + "\" Fehler: " + e.Message);
//return null;
throw new Exception("Konnte keine Verbindung zum Server herstellen: " + e.Message);
}
}
return ret;
}
}
}

View File

@ -1,243 +1,217 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{95D6F48A-9488-42A6-A973-941B45B26DB8}</ProjectGuid>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Fraunhofer.Fit.IoT.LoraMap</RootNamespace>
<AssemblyName>Lora-Map</AssemblyName>
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>Adminrights.manifest</ApplicationManifest>
<Product>Lora-Map</Product>
<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 - 10.04.2021</Copyright>
<Authors>BlubbFish</Authors>
<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
1.2.8 Improving the UI
1.2.7 Reorganise a lot of things, add Support for Cameradata
1.2.6 New Types of marker for person
1.2.5 Set textsize for every zoomlevel
1.2.4 Can draw Textmarkers on the Map, use MGRS (UTM) on the Map
1.2.3 #9 display polygons and marker on the map
1.2.2 Bugfix, if only recieve panic packet with gps data, update the marker on the map also
1.2.1 #6 Load the map from the Device
1.2.0 #4 Possible to Ex and Import Setting
1.1.7 #8 Editor for Names
1.1.6 #5 Create admin area
1.1.5 Add support for alert button
1.1.4 #3 Create icons for devices
1.1.3 #1 Click on icon and show details
1.1.2 #2 Show versions number in Site
1.1.1 Add Debian package config</PackageReleaseNotes>
<PackageTags>lora mqtt map lagekarte</PackageTags>
<StartupObject>Fraunhofer.Fit.IoT.LoraMap.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<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>
<Compile Include="Model\Admin\AdminModel.cs" />
<Compile Include="Model\Admin\AdminSession.cs" />
<Compile Include="Model\Camera.cs" />
<Compile Include="Model\Crowd.cs" />
<Compile Include="Model\Marker.cs" />
<Compile Include="Model\AlarmItem.cs" />
<Compile Include="Model\UTMData.cs" />
<Compile Include="Server.cs" />
<Compile Include="Model\PositionItem.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Content Include="../CHANGELOG.md" />
<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>
<None Include="Adminrights.manifest" />
<None Include="..\CHANGELOG" />
<None Include="config-example\settings.conf.example">
<None Update="config-example\settings.conf.example">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="dpkg\control" />
<None Include="dpkg\create-Builds.bat" />
<None Include="dpkg\loramap-logrotate" />
<None Include="dpkg\loramap.service" />
<None Include="dpkg\make-deb.sh" />
<None Include="dpkg\postinst" />
<None Include="dpkg\preinst" />
<None Include="dpkg\prerm" />
<None Include="resources\icons\marker\din1451m.woff">
<None Update="resources\admin\css\global.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="resources\js\leaflet\leaflet-src.js.map" />
</ItemGroup>
<ItemGroup>
<Content Include="resources\admin\css\global.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\admin\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\admin\login.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\css\global.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\css\icons\admin-with-cogwheels.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\css\icons\cctv.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\css\icons\failtile.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\css\icons\information.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\css\icons\placeholder.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\akku\0-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\akku\1-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\akku\2-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\akku\3-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\akku\4-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\add.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\bullet_add.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\bullet_delete.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\bullet_star.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\edit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\icon_edit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\remove.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\general\save.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\marker\map-marker.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\marker\Marker.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\icons\marker\Person.svg">
<SubType>Designer</SubType>
</Content>
<Content Include="resources\js\functions.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\images\layers-2x.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\images\layers.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\images\marker-icon-2x.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\images\marker-icon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\images\marker-shadow.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\leaflet-src.js" />
<Content Include="resources\js\leaflet\leaflet.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\leaflet.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\menu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\map.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\marker.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="config-example\requests.conf.example">
<None Update="resources\admin\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="resources\favicon.ico">
<None Update="resources\admin\js\adminmenu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\index.html">
</None>
<None Update="resources\admin\js\eximport.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp.csproj">
<Project>{da8510ce-7899-49dd-9e17-7c974382288d}</Project>
<Name>CoordinateSharp</Name>
</ProjectReference>
<ProjectReference Include="..\..\Librarys\litjson\litjson\litjson_4.7.1.csproj">
<Project>{91a14cd2-2940-4500-8193-56d37edddbaa}</Project>
<Name>litjson_4.7.1</Name>
</ProjectReference>
<ProjectReference Include="..\..\Librarys\mqtt\M2Mqtt\M2Mqtt_4.7.1.csproj">
<Project>{a11aef5a-b246-4fe8-8330-06db73cc8074}</Project>
<Name>M2Mqtt_4.7.1</Name>
</ProjectReference>
<ProjectReference Include="..\..\Utils\Bot-Utils\Bot-Utils\Bot-Utils.csproj">
<Project>{bb7bfcb5-3db0-49e1-802a-3ce3eecc59f9}</Project>
<Name>Bot-Utils</Name>
</ProjectReference>
<ProjectReference Include="..\..\Utils\ConnectorDataMqtt\ConnectorDataMqtt\ConnectorDataMqtt.csproj">
<Project>{ee6c8f68-ed46-4c1c-abdd-cfcdf75104f2}</Project>
<Name>ConnectorDataMqtt</Name>
</ProjectReference>
<ProjectReference Include="..\..\Utils\Utils-IoT\Utils-IoT\Utils-IoT.csproj">
<Project>{b870e4d5-6806-4a0b-b233-8907eedc5afc}</Project>
<Name>Utils-IoT</Name>
</ProjectReference>
<ProjectReference Include="..\..\Utils\Utils\Utils\Utils.csproj">
<Project>{fac8ce64-bf13-4ece-8097-aeb5dd060098}</Project>
<Name>Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="resources\admin\js\menu.js">
</None>
<None Update="resources\admin\js\nameseditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="resources\js\overlays.js">
</None>
<None Update="resources\admin\js\settings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</None>
<None Update="resources\admin\login.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\global.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\admin-with-cogwheels.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\cctv.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\failtile.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\filter.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\information.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\placeholder.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\search.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\storm-ac.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\css\icons\storm-in.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\akku\0-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\akku\1-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\akku\2-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\akku\3-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\akku\4-4.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\add.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\bullet_add.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\bullet_delete.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\bullet_star.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\edit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\icon_edit.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\remove.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\general\save.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\marker\din1451m.woff">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\marker\map-marker.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\icons\marker\Marker.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\functions.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\leaflet\images\layers-2x.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\leaflet\images\layers.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\leaflet\images\marker-icon-2x.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\leaflet\images\marker-icon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\leaflet\images\marker-shadow.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\leaflet\leaflet.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\leaflet\leaflet.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\map.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\marker.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\menu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\js\overlays.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Helper\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View File

@ -1,157 +1,146 @@
using System;
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);
public event AdminEvent NamesUpdate;
private readonly Dictionary<Int64, AdminSession> session = new Dictionary<Int64, AdminSession>();
private readonly Dictionary<String, String> settings;
public AdminModel(Dictionary<String, String> settings) {
this.settings = settings;
if(!settings.ContainsKey("admin_user") || !settings.ContainsKey("admin_pass")) {
Helper.WriteError("Kann die Einstellungen [webserver] admin_user und admin_pass nicht laden!");
throw new FileNotFoundException("Kann die Einstellungen [webserver] admin_user und admin_pass nicht laden!");
}
}
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/get_json_")) {
return this.SendJson(cont);
}
if(cont.Request.Url.PathAndQuery.StartsWith("/admin/set_json_")) {
return this.GetJson(cont);
}
return Webserver.SendFileResponse(cont);
}
private Boolean GetJson(HttpListenerContext cont) {
if(cont.Request.Url.PathAndQuery == "/admin/set_json_names") {
StreamReader reader = new StreamReader(cont.Request.InputStream, cont.Request.ContentEncoding);
String rawData = reader.ReadToEnd();
cont.Request.InputStream.Close();
reader.Close();
try {
JsonMapper.ToObject(rawData);
} catch(Exception) {
Helper.WriteError("501 - Error recieving names.json " + cont.Request.Url.PathAndQuery);
cont.Response.StatusCode = 501;
return false;
}
File.WriteAllText("json/names.json", rawData);
Console.WriteLine("200 - Post names.json " + cont.Request.Url.PathAndQuery);
this.NamesUpdate?.Invoke(this, new EventArgs());
return true;
} else if(cont.Request.Url.PathAndQuery == "/admin/set_json_geo") {
StreamReader reader = new StreamReader(cont.Request.InputStream, cont.Request.ContentEncoding);
String rawData = reader.ReadToEnd();
cont.Request.InputStream.Close();
reader.Close();
try {
JsonMapper.ToObject(rawData);
} catch(Exception) {
Helper.WriteError("501 - Error recieving geo.json " + cont.Request.Url.PathAndQuery);
cont.Response.StatusCode = 501;
return false;
}
File.WriteAllText("json/geo.json", rawData);
Console.WriteLine("200 - Post geo.json " + cont.Request.Url.PathAndQuery);
this.NamesUpdate?.Invoke(this, new EventArgs());
return true;
}
return false;
}
private Boolean SendJson(HttpListenerContext cont) {
if(cont.Request.Url.PathAndQuery == "/admin/get_json_names") {
String file = File.ReadAllText("json/names.json");
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);
return true;
} else if(cont.Request.Url.PathAndQuery == "/admin/get_json_geo") {
String file = File.ReadAllText("json/geo.json");
Byte[] buf = Encoding.UTF8.GetBytes(file);
cont.Response.ContentLength64 = buf.Length;
cont.Response.OutputStream.Write(buf, 0, buf.Length);
Console.WriteLine("200 - Send geo.json " + cont.Request.Url.PathAndQuery);
return true;
}
Helper.WriteError("404 - Section in get_json not found " + cont.Request.Url.PathAndQuery + "!");
cont.Response.StatusCode = 404;
return false;
}
private Boolean Login(HttpListenerContext cont) {
Dictionary<String, String> POST = Webserver.GetPostParams(cont.Request);
if(POST.ContainsKey("user") && POST["user"] == this.settings["admin_user"] && POST.ContainsKey("pass") && POST["pass"] == this.settings["admin_pass"]) {
Int64 sessionid = 0;
while(true) {
sessionid = AdminSession.GetRandomSessionid();
if(!this.session.ContainsKey(sessionid)) {
break;
}
}
if(cont.Request.Cookies["loramapsession"] != null) {
if(Int64.TryParse(cont.Request.Cookies["loramapsession"].Value, out Int64 cookiesessionid)) {
if(this.session.ContainsKey(cookiesessionid)) {
if(!this.session[sessionid].IsLoggedin) {
sessionid = cookiesessionid;
}
}
}
}
if(!this.session.ContainsKey(sessionid)) {
this.session.Add(sessionid, new AdminSession());
}
this.session[sessionid].IsLoggedin = true;
cont.Response.AppendCookie(new Cookie("loramapsession", sessionid.ToString()) {
Expires = DateTime.Now.AddYears(1)
});
cont.Response.AddHeader("Location", "/admin");
cont.Response.StatusCode = 307;
Console.WriteLine("200 - Login OK! " + cont.Request.Url.PathAndQuery);
return true;
}
cont.Response.AddHeader("Location", "/admin/login.html");
cont.Response.StatusCode = 307;
Helper.WriteError("307 - Login WRONG! " + cont.Request.Url.PathAndQuery);
return false;
}
private Boolean CheckAuth(HttpListenerContext cont) {
#if DEBUG
return true;
#endif
if(cont.Request.Url.PathAndQuery.StartsWith("/admin/login.html")) {
return true;
} else {
if(cont.Request.Cookies["loramapsession"] != null) {
if(Int64.TryParse(cont.Request.Cookies["loramapsession"].Value, out Int64 sessionid)) {
if(this.session.ContainsKey(sessionid)) {
return this.session[sessionid].IsLoggedin;
}
}
}
cont.Response.StatusCode = 403;
Helper.WriteError("403 - " + cont.Request.Url.PathAndQuery);
}
return false;
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
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;
public AdminModel(Dictionary<String, String> settings) {
this.settings = settings;
if(!settings.ContainsKey("admin_user") || !settings.ContainsKey("admin_pass")) {
Helper.WriteError("Kann die Einstellungen [webserver] admin_user und admin_pass nicht laden!");
throw new FileNotFoundException("Kann die Einstellungen [webserver] admin_user und admin_pass nicht laden!");
}
}
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 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();
reader.Close();
try {
_ = JsonMapper.ToObject(rawData);
} catch (Exception) {
Helper.WriteError("501 - Error recieving " + filename + ", no valid json " + cont.Request.Url.PathAndQuery);
cont.Response.StatusCode = 501;
return false;
}
File.WriteAllText(filename, rawData);
Console.WriteLine("200 - PUT " + filename + " " + cont.Request.Url.PathAndQuery);
this.GetEvent<AdminEvent>(updatenotifier)?.Invoke(this, new EventArgs());
return true;
}
private Boolean Login(HttpListenerContext cont) {
Dictionary<String, String> POST = Webserver.GetPostParams(cont.Request);
if(POST.ContainsKey("user") && POST["user"] == this.settings["admin_user"] && POST.ContainsKey("pass") && POST["pass"] == this.settings["admin_pass"]) {
Int64 sessionid;
while(true) {
sessionid = AdminSession.GetRandomSessionid();
if(!this.session.ContainsKey(sessionid)) {
break;
}
}
if(cont.Request.Cookies["loramapsession"] != null) {
if(Int64.TryParse(cont.Request.Cookies["loramapsession"].Value, out Int64 cookiesessionid)) {
if(this.session.ContainsKey(cookiesessionid)) {
if(!this.session[sessionid].IsLoggedin) {
sessionid = cookiesessionid;
}
}
}
}
if(!this.session.ContainsKey(sessionid)) {
this.session.Add(sessionid, new AdminSession());
}
this.session[sessionid].IsLoggedin = true;
cont.Response.AppendCookie(new Cookie("loramapsession", sessionid.ToString()) {
Expires = DateTime.Now.AddYears(1)
});
cont.Response.AddHeader("Location", "/admin");
cont.Response.StatusCode = 307;
Console.WriteLine("200 - Login OK! " + cont.Request.Url.PathAndQuery);
return true;
}
cont.Response.AddHeader("Location", "/admin/login.html");
cont.Response.StatusCode = 307;
Helper.WriteError("307 - Login WRONG! " + cont.Request.Url.PathAndQuery);
return false;
}
private Boolean CheckAuth(HttpListenerContext cont) {
#if DEBUG
Helper.WriteError("200 - AUTH-Bypassed!");
return true;
#else
if(cont.Request.Url.PathAndQuery.StartsWith("/admin/login.html")) {
return true;
} else {
if(cont.Request.Cookies["loramapsession"] != null) {
if(Int64.TryParse(cont.Request.Cookies["loramapsession"].Value, out Int64 sessionid)) {
if(this.session.ContainsKey(sessionid)) {
return this.session[sessionid].IsLoggedin;
}
}
}
cont.Response.StatusCode = 403;
Helper.WriteError("403 - " + cont.Request.Url.PathAndQuery);
}
return false;
#endif
}
}
}

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

@ -1,81 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
class AlarmItem {
public Double Rssi { get; private set; }
public Double Snr { get; private set; }
public DateTime Lorarecievedtime { get; private set; }
public DateTime Recievedtime { get; private set; }
public Double Latitude { get; private set; }
public Double Longitude { get; private set; }
public UTMData UTM { get; private set; }
public Double Hdop { get; private set; }
public Boolean Fix { get; private set; }
public Double Height { get; private set; }
public List<DateTime> ButtonPressed => this.buttonhistory.Keys.ToList();
private readonly SortedDictionary<DateTime, String> buttonhistory = new SortedDictionary<DateTime, String>();
public AlarmItem(JsonData json) => this.Update(json);
public void Update(JsonData json) {
this.Rssi = (Double)json["Rssi"];
this.Snr = (Double)json["Snr"];
if(DateTime.TryParse((String)json["Receivedtime"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
this.Lorarecievedtime = updatetime.ToUniversalTime();
}
this.Recievedtime = DateTime.UtcNow;
this.Latitude = (Double)json["Gps"]["Latitude"];
this.Longitude = (Double)json["Gps"]["Longitude"];
this.Fix = (Boolean)json["Gps"]["Fix"];
if(!this.Fix) {
this.Latitude = (Double)json["Gps"]["LastLatitude"];
this.Longitude = (Double)json["Gps"]["LastLongitude"];
}
this.UTM = new UTMData(this.Latitude, this.Longitude);
this.Hdop = (Double)json["Gps"]["Hdop"];
this.Height = (Double)json["Gps"]["Height"];
this.SetHistory(json);
}
private void SetHistory(JsonData json) {
String key = ((Double)json["BatteryLevel"]).ToString();
key += "_" + ((Int32)json["Calculatedcrc"]).ToString();
key += "_" + ((Double)json["Gps"]["Hdop"]).ToString();
key += "_" + ((Double)json["Gps"]["Height"]).ToString();
key += "_" + ((Boolean)json["Gps"]["Fix"]).ToString();
key += "_" + ((Double)json["Gps"]["LastLatitude"]).ToString();
key += "_" + ((Double)json["Gps"]["LastLongitude"]).ToString();
key += "_" + ((String)json["Gps"]["Time"]);
if(!this.buttonhistory.ContainsValue(key)) {
this.buttonhistory.Add(DateTime.UtcNow, key);
if(this.buttonhistory.Count > 10) {
this.buttonhistory.Remove(this.buttonhistory.Keys.ToList().First());
}
}
}
public static String GetId(JsonData json) => (String)json["Name"];
public static Boolean CheckJson(JsonData json) =>
json.ContainsKey("Rssi") && json["Rssi"].IsDouble &&
json.ContainsKey("Snr") && json["Snr"].IsDouble &&
json.ContainsKey("Receivedtime") && json["Receivedtime"].IsString &&
json.ContainsKey("Gps") && json["Gps"].IsObject &&
json["Gps"].ContainsKey("Latitude") && json["Gps"]["Latitude"].IsDouble &&
json["Gps"].ContainsKey("Longitude") && json["Gps"]["Longitude"].IsDouble &&
json["Gps"].ContainsKey("LastLatitude") && json["Gps"]["LastLatitude"].IsDouble &&
json["Gps"].ContainsKey("LastLongitude") && json["Gps"]["LastLongitude"].IsDouble &&
json["Gps"].ContainsKey("Hdop") && json["Gps"]["Hdop"].IsDouble &&
json["Gps"].ContainsKey("Fix") && json["Gps"]["Fix"].IsBoolean &&
json["Gps"].ContainsKey("Height") && json["Gps"]["Height"].IsDouble &&
json["Gps"].ContainsKey("Time") && json["Gps"]["Time"].IsString &&
json.ContainsKey("Name") && json["Name"].IsString &&
json.ContainsKey("Calculatedcrc") && json["Calculatedcrc"].IsInt &&
json.ContainsKey("BatteryLevel") && json["BatteryLevel"].IsDouble;
}
}

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

@ -1,63 +1,49 @@
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.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 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 &&
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("timestamp_1") && json["timestamp_1"].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["timestamp_1"], 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;
}
}
}

View File

@ -0,0 +1,35 @@
using LitJson;
using System;
using System.Globalization;
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; }
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;
}
}
}
}

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

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LitJson;
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 PositionAlarm(JsonData json) : base(json, null) {
}
public override void Update(JsonData json) {
base.Update(json);
this.SetHistory(json);
}
private void SetHistory(JsonData json) {
if(json.ContainsKey("Hash") && json["Hash"].IsString) {
String key = json["Hash"].ToString();
if(!this.buttonhistory.ContainsValue(key)) {
this.buttonhistory.Add(DateTime.UtcNow, key);
if(this.buttonhistory.Count > 10) {
_ = this.buttonhistory.Remove(this.buttonhistory.Keys.ToList().First());
}
}
}
}
}
}

View File

@ -0,0 +1,102 @@
using System;
using System.Globalization;
using Fraunhofer.Fit.IoT.LoraMap.Model.Svg;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Position {
public class PositionItem {
private Double _lastLat = 0;
private Double _lastLon = 0;
public Double Rssi { get; private set; }
public Double Snr { get; private set; }
public DateTime Lorarecievedtime { get; private set; }
public DateTime Recievedtime { get; private set; }
public Double Latitude { get; private set; }
public Double Longitude { get; private set; }
public UTMData UTM { get; private set; }
public Double Hdop { get; private set; }
public DateTime Lastgpspostime { get; private set; }
public Double Battery { get; private set; }
public Int32 Batterysimple { get; private set; }
public Boolean Fix { get; private set; }
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) {
this.Update(json);
this.UpdateMarker(marker, GetId(json));
}
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;
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;
this.Icon = null;
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
&& json["Gps"].ContainsKey("Latitude") && (json["Gps"]["Latitude"].IsDouble || json["Gps"]["Latitude"].IsInt)
&& json["Gps"].ContainsKey("Longitude") && (json["Gps"]["Longitude"].IsDouble || json["Gps"]["Longitude"].IsInt)
&& json["Gps"].ContainsKey("Fix") && json["Gps"]["Fix"].IsBoolean
&& json["Gps"].ContainsKey("Height") && (json["Gps"]["Height"].IsDouble || json["Gps"]["Height"].IsInt)
&& json.ContainsKey("Name") && json["Name"].IsString;
public static String GetId(JsonData json) => (String)json["Name"];
public virtual void Update(JsonData json) {
this.Rssi = json.ContainsKey("Rssi") && (json["Rssi"].IsDouble || json["Rssi"].IsInt) && Double.TryParse(json["Rssi"].ToString(), out Double rssi) ? rssi : 0;
this.Snr = json.ContainsKey("Snr") && (json["Snr"].IsDouble || json["Snr"].IsInt) && Double.TryParse(json["Snr"].ToString(), out Double snr) ? snr : 0;
this.Lorarecievedtime = json.ContainsKey("Receivedtime") && json["Receivedtime"].IsString && DateTime.TryParse((String)json["Receivedtime"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime) ? updatetime.ToUniversalTime() : DateTime.UtcNow;
this.Recievedtime = DateTime.UtcNow;
this.Battery = Math.Round(json["BatteryLevel"].IsInt ? (Int32)json["BatteryLevel"] : (Double)json["BatteryLevel"], 2);
this.Batterysimple = this.Battery < 3.44 ? 0 : this.Battery < 3.53 ? 1 : this.Battery < 3.6525 ? 2 : this.Battery < 3.8825 ? 3 : 4;
this.Latitude = json["Gps"]["Latitude"].IsInt ? (Int32)json["Gps"]["Latitude"] : (Double)json["Gps"]["Latitude"];
this.Longitude = json["Gps"]["Longitude"].IsInt ? (Int32)json["Gps"]["Longitude"] : (Double)json["Gps"]["Longitude"];
this.Fix = (Boolean)json["Gps"]["Fix"];
this.Height = json["Gps"]["Height"].IsInt ? (Int32)json["Gps"]["Height"] : (Double)json["Gps"]["Height"];
this.Hdop = json["Gps"].ContainsKey("Hdop") && (json["Gps"]["Hdop"].IsDouble || json["Gps"]["Hdop"].IsInt) && Double.TryParse(json["Gps"]["Hdop"].ToString(), out Double hdop) ? hdop : 0;
if(!this.Fix) {
this.Latitude = this._lastLat;
this.Longitude = this._lastLon;
} else {
this._lastLat = this.Latitude;
this._lastLon = this.Longitude;
this.Lastgpspostime = DateTime.UtcNow;
}
this.UTM = new UTMData(this.Latitude, this.Longitude);
}
}
}

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

@ -1,102 +0,0 @@
using System;
using System.Globalization;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
class PositionItem {
public Double Rssi { get; private set; }
public Double Snr { get; private set; }
public DateTime Lorarecievedtime { get; private set; }
public DateTime Recievedtime { get; private set; }
public Double Latitude { get; private set; }
public Double Longitude { get; private set; }
public UTMData UTM { get; private set; }
public Double Hdop { get; private set; }
public DateTime Lastgpspostime { get; private set; }
public Double Battery { get; private set; }
public Int32 Batterysimple { get; private set; }
public Boolean Fix { get; private set; }
public Double Height { get; private set; }
public String Name { get; private set; }
public String Icon { get; private set; }
public PositionItem(JsonData json, JsonData marker) {
this.Update(json);
this.UpdateMarker(marker, GetId(json));
}
public void UpdateMarker(JsonData marker, String id) {
if(marker.ContainsKey(id)) {
if(marker[id].ContainsKey("name") && marker[id]["name"].IsString) {
this.Name = (String)marker[id]["name"];
} else {
this.Name = id;
}
if(marker[id].ContainsKey("marker.svg") && marker[id]["marker.svg"].IsObject) {
this.Icon = Marker.ParseMarkerConfig(marker[id]["marker.svg"], this.Name);
} else if(marker[id].ContainsKey("icon") && marker[id]["icon"].IsString) {
this.Icon = (String)marker[id]["icon"];
} else {
this.Icon = null;
}
} else {
this.Name = id;
this.Icon = null;
}
}
public static Boolean CheckJson(JsonData json) => json.ContainsKey("Rssi") && json["Rssi"].IsDouble
&& json.ContainsKey("Snr") && json["Snr"].IsDouble
&& json.ContainsKey("Receivedtime") && json["Receivedtime"].IsString
&& json.ContainsKey("BatteryLevel") && json["BatteryLevel"].IsDouble
&& json.ContainsKey("Gps") && json["Gps"].IsObject
&& json["Gps"].ContainsKey("Latitude") && json["Gps"]["Latitude"].IsDouble
&& json["Gps"].ContainsKey("Longitude") && json["Gps"]["Longitude"].IsDouble
&& json["Gps"].ContainsKey("LastLatitude") && json["Gps"]["LastLatitude"].IsDouble
&& json["Gps"].ContainsKey("LastLongitude") && json["Gps"]["LastLongitude"].IsDouble
&& json["Gps"].ContainsKey("LastGPSPos") && json["Gps"]["LastGPSPos"].IsString
&& json["Gps"].ContainsKey("Hdop") && json["Gps"]["Hdop"].IsDouble
&& json["Gps"].ContainsKey("Fix") && json["Gps"]["Fix"].IsBoolean
&& json["Gps"].ContainsKey("Height") && json["Gps"]["Height"].IsDouble
&& json.ContainsKey("Name") && json["Name"].IsString;
public static String GetId(JsonData json) => (String)json["Name"];
public void Update(JsonData json) {
this.Rssi = (Double)json["Rssi"];
this.Snr = (Double)json["Snr"];
if(DateTime.TryParse((String)json["Receivedtime"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
this.Lorarecievedtime = updatetime.ToUniversalTime();
}
this.Recievedtime = DateTime.UtcNow;
this.Battery = Math.Round((Double)json["BatteryLevel"], 2);
if(this.Battery < 3.44) {
this.Batterysimple = 0;
} else if(this.Battery < 3.53) {
this.Batterysimple = 1;
} else if(this.Battery < 3.6525) {
this.Batterysimple = 2;
} else if(this.Battery < 3.8825) {
this.Batterysimple = 3;
} else {
this.Batterysimple = 4;
}
this.Latitude = (Double)json["Gps"]["Latitude"];
this.Longitude = (Double)json["Gps"]["Longitude"];
this.Fix = (Boolean)json["Gps"]["Fix"];
if(!this.Fix) {
this.Latitude = (Double)json["Gps"]["LastLatitude"];
this.Longitude = (Double)json["Gps"]["LastLongitude"];
}
this.UTM = new UTMData(this.Latitude, this.Longitude);
if(DateTime.TryParse((String)json["Gps"]["LastGPSPos"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime lastgpstime)) {
this.Lastgpspostime = lastgpstime.ToUniversalTime();
}
this.Hdop = (Double)json["Gps"]["Hdop"];
this.Height = (Double)json["Gps"]["Height"];
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Globalization;
using LitJson;
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; }
public Double Temperature { get; private set; }
public Double Humidity { get; private set; }
public Double Windspeed { get; private set; }
public DateTime Lorarecievedtime { get; private set; }
public SensorEnviromentData(JsonData json) => this.Update(json);
public void Update(JsonData json) {
this.Name = GetId(json);
this.Rssi = json["Rssi"].IsInt ? (Int32)json["Rssi"] : (Double)json["Rssi"];
this.Snr = json["Snr"].IsInt ? (Int32)json["Snr"] : (Double)json["Snr"];
this.Temperature = json["Temperature"].IsInt ? (Int32)json["Temperature"] : (Double)json["Temperature"];
this.Humidity = json["Humidity"].IsInt ? (Int32)json["Humidity"] : (Double)json["Humidity"];
this.Windspeed = json["Windspeed"].IsInt ? (Int32)json["Windspeed"] : (Double)json["Windspeed"];
if(DateTime.TryParse((String)json["Receivedtime"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
this.Lorarecievedtime = updatetime.ToUniversalTime();
}
}
public static Boolean CheckJson(JsonData json) =>
json.ContainsKey("Name") && json["Name"].IsString &&
json.ContainsKey("Rssi") && (json["Rssi"].IsDouble || json["Rssi"].IsInt) &&
json.ContainsKey("Snr") && (json["Snr"].IsDouble || json["Snr"].IsInt) &&
json.ContainsKey("Temperature") && (json["Temperature"].IsDouble || json["Temperature"].IsInt) &&
json.ContainsKey("Humidity") && (json["Humidity"].IsDouble || json["Humidity"].IsInt) &&
json.ContainsKey("Windspeed") && (json["Windspeed"].IsDouble || json["Windspeed"].IsInt) &&
json.ContainsKey("Receivedtime") && json["Receivedtime"].IsString;
public static String GetId(JsonData json) => (String)json["Name"];
}
}

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

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Fraunhofer.Fit.IoT.LoraMap.Lib;
using LitJson;
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 SensorWeather() => this.StartBackgroundThread();
private void StartBackgroundThread() {
this.Warnungen = new List<Warning>();
this.bgthread = new Thread(this.BackGroundRunner);
this.backgroundrunnerAlive = true;
this.bgthread.Start();
}
private void BackGroundRunner() {
while(this.backgroundrunnerAlive) {
List<Warning> ret = new List<Warning>();
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) {
foreach (JsonData warning in json["features"]) {
try {
ret.Add(new Warning(warning));
} catch { }
}
}
} catch { }
}
this.Warnungen = ret;
for (Int32 i = 0; i < 1000; i++) {
if (this.backgroundrunnerAlive) {
Thread.Sleep(60);
}
}
}
}
public void Dispose() {
try {
this.backgroundrunnerAlive = false;
while (this.bgthread != null && this.bgthread.IsAlive) {
Thread.Sleep(10);
}
this.bgthread = null;
} catch { }
}
public class Warning {
public Warning(JsonData warning) {
this.Id = warning["id"].ToString();
this.From = warning["properties"]["SENT"].ToString();
this.To = warning["properties"]["EXPIRES"].ToString();
this.Location = warning["properties"]["NAME"].ToString();
this.Type = warning["properties"]["EVENT"].ToString().ToLower();
this.Level = warning["properties"]["SEVERITY"].ToString().ToLower();
this.Headline = warning["properties"]["HEADLINE"].ToString();
this.Body = warning["properties"]["DESCRIPTION"].ToString();
this.Instructions = warning["properties"]["INSTRUCTION"] != null ? warning["properties"]["INSTRUCTION"].ToString() : "";
}
public String Id { get; }
public String From { get; }
public String To { get; }
public String Location { get; }
public String Type { get; }
public String Level { get; }
public String Headline { get; }
public String Body { get; }
public String Instructions { get; }
}
}
}
/* https://maps.dwd.de/geoserver/wfs?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typeName=dwd:Warnungen_Gemeinden&outputFormat=application/json&cql_filter=WARNCELLID=805314000
* {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "Warnungen_Gemeinden.805314000.2.49.0.1.276.0.DWD.PVW.1565627520000.4edfa973-5fef-4b97-8990-7489828dbe5b.DEU",
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[ 7.2072, 50.7395 ],
[ 7.1534, 50.7599 ],
[ 7.1255, 50.7744 ],
[ 7.105, 50.7622 ],
[ 7.0768, 50.7679 ],
[ 7.0666, 50.7705 ],
[ 7.0378, 50.7558 ],
[ 7.0256, 50.7054 ],
[ 7.0385, 50.6886 ],
[ 7.0255, 50.665 ],
[ 7.0473, 50.6391 ],
[ 7.0639, 50.6309 ],
[ 7.1054, 50.6595 ],
[ 7.1278, 50.6472 ],
[ 7.1564, 50.6547 ],
[ 7.1954, 50.6434 ],
[ 7.2119, 50.649 ],
[ 7.1972, 50.6648 ],
[ 7.1679, 50.7035 ],
[ 7.1969, 50.7138 ],
[ 7.2072, 50.7395 ]
]
]
]
},
"geometry_name": "THE_GEOM",
"properties": {
"AREADESC": "Bonn",
"NAME": "Stadt Bonn",
"WARNCELLID": 805314000,
"IDENTIFIER": "2.49.0.1.276.0.DWD.PVW.1565627520000.4edfa973-5fef-4b97-8990-7489828dbe5b.DEU",
"SENDER": "CAP@dwd.de",
"SENT": "2019-08-12T16:32:00Z",
"STATUS": "Actual",
"MSGTYPE": "Update",
"SOURCE": "PVW",
"SCOPE": "Public",
"CODE": "id:2.49.0.1.276.0.DWD.PVW.1565627520000.4edfa973-5fef-4b97-8990-7489828dbe5b",
"LANGUAGE": "de-DE",
"CATEGORY": "Met",
"EVENT": "GEWITTER",
"RESPONSETYPE": "Prepare",
"URGENCY": "Immediate",
"SEVERITY": "Minor",
"CERTAINTY": "Likely",
"EC_PROFILE": "2.1",
"EC_LICENSE": "Geobasisdaten: Copyright Bundesamt für Kartographie und Geodäsie, Frankfurt am Main, 2017",
"EC_II": "31",
"EC_GROUP": "THUNDERSTORM;WIND",
"EC_AREA_COLOR": "255 255 0",
"EFFECTIVE": "2019-08-12T16:32:00Z",
"ONSET": "2019-08-12T16:32:00Z",
"EXPIRES": "2019-08-12T17:00:00Z",
"SENDERNAME": "DWD / Nationales Warnzentrum Offenbach",
"HEADLINE": "Amtliche WARNUNG vor GEWITTER",
"DESCRIPTION": "Von Südwesten ziehen einzelne Gewitter auf. Dabei gibt es Windböen mit Geschwindigkeiten um 60 km/h (17m/s, 33kn, Bft 7).",
"INSTRUCTION": "ACHTUNG! Hinweis auf mögliche Gefahren: Örtlich kann es Blitzschlag geben. Bei Blitzschlag besteht Lebensgefahr!",
"WEB": "http://www.wettergefahren.de",
"CONTACT": "Deutscher Wetterdienst",
"PARAMETERNAME": "Böen;Gewitter;Gewitteraufzugsrichtung",
"PARAMETERVALUE": "~60 [km/h];einzelne;SW",
"ALTITUDE": 0,
"CEILING": 9842.5197,
"bbox": [ 7.0255, 50.6309, 7.2119, 50.7744 ]
}
}
],
"totalFeatures": 1,
"numberMatched": 1,
"numberReturned": 1,
"timeStamp": "2019-08-12T16:42:20.743Z",
"crs": {
"type": "name",
"properties": { "name": "urn:ogc:def:crs:EPSG::4326" }
},
"bbox": [ 50.6309, 7.0255, 50.7744, 7.2119 ]
}
*/

328
Lora-Map/Model/Settings.cs Normal file
View File

@ -0,0 +1,328 @@
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 : OwnSingeton<Settings> {
private Int32 gridradius;
public PublicSettings External {
get; set;
}
public PrivateSettings Internal {
get; set;
}
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"]["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.External.Startloclat = 0;
this.External.Startloclon = 0;
}
this.Internal.WeatherCellIDs.Clear();
if(json.ContainsKey("CellIds") && json["CellIds"].IsArray && json["CellIds"].Count > 0) {
foreach(JsonData item in json["CellIds"]) {
if(Int32.TryParse(item.ToString(), out Int32 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"]) {
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"]) {
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"]);
}
fight.Polygon.Add(coords);
}
}
if(entry.Value.ContainsKey("Level") && entry.Value["Level"].IsDouble) {
fight.Level = (Double)entry.Value["Level"];
}
if(entry.Value.ContainsKey("Alias") && entry.Value["Alias"].IsString) {
fight.Alias = (String)entry.Value["Alias"];
}
fights.Add(entry.Key, fight);
}
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"]) {
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"]) {
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"]);
}
density.Polygon.Add(coords);
}
}
if(entry.Value.ContainsKey("Count") && (entry.Value["Count"].IsInt || entry.Value["Count"].IsDouble)) {
density.Maximum = (Int32)entry.Value["Count"];
}
if(entry.Value.ContainsKey("Alias") && entry.Value["Alias"].IsString) {
density.Alias = (String)entry.Value["Alias"];
}
densitys.Add(entry.Key, density);
}
this.External.DensityArea = densitys;
}
if(json.ContainsKey("Sensors") && json["Sensors"].IsObject) {
Dictionary<String, Sensor> sensors = new Dictionary<String, Sensor>();
foreach(KeyValuePair<String, JsonData> entry in json["Sensors"]) {
Sensor sensor = new Sensor {
Coordinates = new List<Double>()
};
if(entry.Value.ContainsKey("Poly") && entry.Value["Poly"].IsObject) {
JsonData coord = 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"]);
}
sensor.Coordinates = coords;
if(entry.Value.ContainsKey("Level") && (entry.Value["Level"].IsInt || entry.Value["Level"].IsDouble)) {
sensor.Level = entry.Value["Level"].IsInt ? (Int32)entry.Value["Level"] : (Double)entry.Value["Level"];
}
if(entry.Value.ContainsKey("Alias") && entry.Value["Alias"].IsString) {
sensor.Alias = (String)entry.Value["Alias"];
}
sensors.Add(entry.Key, sensor);
}
}
this.External.Sensors = sensors;
}
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.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.External.Startloclat == 0 || this.External.Startloclon == 0 || this.gridradius == 0) {
return;
}
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) {
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.External.Grid["Major"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
TopLeft.Latitude.DecimalDegree,
TopLeft.Longitude.DecimalDegree
}
},
{ "to", new List<Double> {
BottomLeft.Latitude.DecimalDegree,
BottomLeft.Longitude.DecimalDegree
}
}
});
} else {
this.External.Grid["Minor"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
TopLeft.Latitude.DecimalDegree,
TopLeft.Longitude.DecimalDegree
}
},
{ "to", new List<Double> {
BottomLeft.Latitude.DecimalDegree,
BottomLeft.Longitude.DecimalDegree
}
}
});
}
}
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.External.Grid["Major"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
BottomLeft.Latitude.DecimalDegree,
BottomLeft.Longitude.DecimalDegree
}
},
{ "to", new List<Double> {
BottomRight.Latitude.DecimalDegree,
BottomRight.Longitude.DecimalDegree
}
}
});
} else {
this.External.Grid["Minor"].Add(new Dictionary<String, List<Double>> {
{ "from", new List<Double> {
BottomLeft.Latitude.DecimalDegree,
BottomLeft.Longitude.DecimalDegree
}
},
{ "to", new List<Double> {
BottomRight.Latitude.DecimalDegree,
BottomRight.Longitude.DecimalDegree
}
}
});
}
}
}
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 struct Fight {
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 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

@ -5,26 +5,18 @@ using BlubbFish.Utils.IoT.Connector;
namespace Fraunhofer.Fit.IoT.LoraMap {
class Program {
static void Main(String[] args) {
static void Main(String[] _1) {
InIReader.SetSearchPath(new List<String>() { "/etc/loramap", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\loramap" });
if (!InIReader.ConfigExist("settings")) {
Helper.WriteError("settings.ini not found!");
Console.ReadLine();
return;
}
if(!InIReader.ConfigExist("requests")) {
Helper.WriteError("requests.ini not found!");
Console.ReadLine();
_ = Console.ReadLine();
return;
}
InIReader ini = InIReader.GetInstance("settings");
Dictionary<String, String> backenddata = ini.GetSection("mqtt");
backenddata.Add("topic", "lora/#;camera/#");
backenddata.Add("topic", "lora/#;camera/#;sfn/#");
ADataBackend b = (ADataBackend)ABackend.GetInstance(backenddata, ABackend.BackendType.Data);
new Server(b, ini.GetSection("webserver"), InIReader.GetInstance("requests"));
while(true) {
System.Threading.Thread.Sleep(1000);
}
_ = new Server(b, ini.GetSection("webserver"));
}
}
}

View File

@ -1,56 +0,0 @@
using System.Resources;
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("Lora-Map")]
[assembly: AssemblyDescription("Displays Items with Coordinates from Mqtt on a Map")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Fraunhofer FIT")]
[assembly: AssemblyProduct("Lora-Map")]
[assembly: AssemblyCopyright("Copyright © 2018 - 10.07.2019")]
[assembly: AssemblyTrademark("Fraunhofer FIT, BlubbFish")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("de-DE")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("95d6f48a-9488-42a6-a973-941b45b26db8")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.8")]
[assembly: AssemblyFileVersion("1.2.8")]
/*
* 1.1.1 Add Debian package config
* 1.1.2 #2 Show versions number in Site
* 1.1.3 #1 Click on icon and show details
* 1.1.4 #3 Create icons for devices
* 1.1.5 Add support for alert button
* 1.1.6 #5 Create admin area
* 1.1.7 #8 Editor for Names
* 1.2.0 #4 Possible to Ex and Import Setting
* 1.2.1 #6 Load the map from the Device
* 1.2.2 Bugfix, if only recieve panic packet with gps data, update the marker on the map also
* 1.2.3 #9 display polygons and marker on the map
* 1.2.4 Can draw Textmarkers on the Map, use MGRS (UTM) on the Map
* 1.2.5 #10 text Letzer Datenempfang is too long when scrollbar is there and #11 set textsize for every zoomlevel
* 1.2.6 New Types of marker for person
* 1.2.7 Reorganise a lot of things, add Support for Cameradata
* 1.2.8 Improving the UI
*/

View File

@ -1,104 +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> cameras = new SortedDictionary<String, Camera>();
private readonly SortedDictionary<String, Crowd> crowds = new SortedDictionary<String, Crowd>();
private JsonData marker;
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();
public Server(ADataBackend backend, Dictionary<String, String> settings, InIReader requests) : base(backend, settings, requests) {
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.admin.SettingsUpdate += Settings.Instance.ReloadSettings;
this.admin.GeoUpdate += Settings.Instance.ReloadGeo;
this.admin.NamesUpdate += PositionModel.Instance.ReloadNames;
this.StartListen();
}
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", "{}");
}
this.WaitForShutdown();
this.Dispose();
}
protected override void Backend_MessageIncomming(Object sender, BackendEvent mqtt) {
try {
JsonData d = JsonMapper.ToObject(mqtt.Message);
if(PositionItem.CheckJson(d) && ((String)mqtt.From).Contains("lora/data")) {
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(AlarmItem.CheckJson(d) && ((String)mqtt.From).Contains("lora/panic")) {
String name = AlarmItem.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(Camera.CheckJson(d) && ((String)mqtt.From).Contains("camera/count")) {
String cameraid = Camera.GetId(d);
if(this.cameras.ContainsKey(cameraid)) {
this.cameras[cameraid].Update(d);
} else {
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)) {
this.crowds[cameraid].Update(d);
} else {
this.crowds.Add(cameraid, new Crowd(d));
}
}
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);
}
@ -106,40 +50,26 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
protected override Boolean SendWebserverResponse(HttpListenerContext cont) {
try {
if(cont.Request.Url.PathAndQuery.StartsWith("/loc")) {
return SendJsonResponse(this.positions, cont);
} else if(cont.Request.Url.PathAndQuery.StartsWith("/panic")) {
return SendJsonResponse(this.alarms, 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("/currenttime")) {
} else if(cont.Request.Url.AbsolutePath.StartsWith("/api/time")) {
return SendJsonResponse(new Dictionary<String, DateTime>() { { "utc", DateTime.UtcNow } }, cont);
} else if(cont.Request.Url.PathAndQuery.StartsWith("/admin")) {
} 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("/getlayer")) {
return SendJsonResponse(this.FindMapLayer(cont.Request), cont);
} else if(cont.Request.Url.PathAndQuery.StartsWith("/maps/")) {
return SendFileResponse(cont, "resources", false);
} else if(cont.Request.Url.PathAndQuery.StartsWith("/getgeo")) {
Byte[] buf = Encoding.UTF8.GetBytes(File.ReadAllText("json/geo.json"));
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("/cameracount")) {
return SendJsonResponse(this.cameras, cont);
} else if (cont.Request.Url.PathAndQuery.StartsWith("/crowdcount")) {
return SendJsonResponse(this.crowds, cont);
}
}
} catch(Exception e) {
Helper.WriteError("SendWebserverResponse(): 500 - " + e.Message + "\n\n" + e.StackTrace);
cont.Response.StatusCode = 500;
@ -148,39 +78,9 @@ namespace Fraunhofer.Fit.IoT.LoraMap {
return SendFileResponse(cont);
}
private Dictionary<String, Dictionary<String, Object>> FindMapLayer(HttpListenerRequest request) {
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", 18 }
} }
};
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() {
SensorModel.Instance.Dispose();
base.Dispose();
}
}
}

View File

@ -1,2 +0,0 @@
[js/map.js]
start_location=50.7, 7.2

View File

@ -3,7 +3,7 @@ Version: x.x-x
Section: base
Priority: optional
Architecture: any
Depends: mono-runtime (>= 5.18), libmono-posix4.0-cil (>= 5.18), libmono-system-web4.0-cil (>= 5.18)
Depends: dotnet-runtime-3.0 (>= 3.0.1)
Maintainer: BlubbFish <dev@blubbfish.net>
Description: Lora-Map
Lora-Map shows a Map to control the Lora Tracker

View File

@ -2,26 +2,35 @@
HOMEDIR=$HOME
ROOT="$HOMEDIR/deb"
OUTPUT="../bin/Release"
OUTPUT="../bin/Release/netcoreapp3.1"
EXEC="$ROOT/usr/local/bin/loramap"
CONFIG="$ROOT/etc/loramap"
DEBNAME="loramap"
CSPROJFILE="Lora-Map.csproj"
EXEC="$ROOT/usr/local/bin/$DEBNAME"
CONFIG="$ROOT/etc/$DEBNAME"
SYSTEMD="$ROOT/lib/systemd/system"
LOGROTATE="$ROOT/etc/logrotate.d"
echo "Catch all paths together for $DEBNAME."
DEBIAN="$ROOT/DEBIAN"
VMAJOR=$(grep -e "^\[assembly: AssemblyVersion(\"" ../Properties/AssemblyInfo.cs | cut -d'"' -f 2 | cut -d'.' -f 1)
VMINOR=$(grep -e "^\[assembly: AssemblyVersion(\"" ../Properties/AssemblyInfo.cs | cut -d'"' -f 2 | cut -d'.' -f 2)
VBUILD=$(grep -e "^\[assembly: AssemblyVersion(\"" ../Properties/AssemblyInfo.cs | cut -d'"' -f 2 | cut -d'.' -f 3)
VMAJOR=$(grep -e "<Version>" ../$CSPROJFILE | cut -d'>' -f 2 | cut -d'<' -f 1 | cut -d'.' -f 1)
VMINOR=$(grep -e "<Version>" ../$CSPROJFILE | cut -d'>' -f 2 | cut -d'<' -f 1 | cut -d'.' -f 2)
VBUILD=$(grep -e "<Version>" ../$CSPROJFILE | cut -d'>' -f 2 | cut -d'<' -f 1 | cut -d'.' -f 3)
ARCHT=$1
echo "Versionsumber parsed: $VMAJOR.$VMINOR-$VBUILD."
mkdir -p $EXEC
mkdir -p $CONFIG
mkdir -p $DEBIAN
mkdir -p $SYSTEMD
mkdir -p $LOGROTATE
cp control $DEBIAN
echo "Created directorys."
cp control $DEBIAN/control
cp preinst $DEBIAN
cp postinst $DEBIAN
cp prerm $DEBIAN
@ -29,26 +38,49 @@ sed -i s/Version:\ x\.x-x/"Version: $VMAJOR.$VMINOR-$VBUILD"/ $DEBIAN/control
sed -i s/Architecture:\ any/"Architecture: $ARCHT"/ $DEBIAN/control
chmod 755 $DEBIAN -R
cp loramap.service $SYSTEMD
chmod 644 $SYSTEMD/loramap.service
echo "Copy deb control files."
cp $OUTPUT/*.exe $EXEC/
#cp $OUTPUT/gpio.2.44 $EXEC/
#cp $OUTPUT/libwiringPi.so.2.44 $EXEC/
cp "service-$DEBNAME" "$SYSTEMD/$DEBNAME.service"
chmod 644 $SYSTEMD/"$DEBNAME.service"
echo "Copy $DEBNAME.service to $SYSTEMD."
cp $OUTPUT/*.runtimeconfig.json $EXEC/
find $OUTPUT -name \*.dll -exec cp {} $EXEC/ \;
chmod 644 $EXEC/*
chmod 755 $EXEC
echo "Copy programm files to $EXEC."
cp $OUTPUT/resources $EXEC -r
sed -i s/"<div id=\"version\">vx.x.x"/"<div id=\"version\">$VMAJOR.$VMINOR.$VBUILD"/ $EXEC/resources/index.html
echo "Change Versionnumber in index.html"
cp $OUTPUT/config-example/* $CONFIG
chmod 644 $CONFIG/*
chmod 755 $CONFIG
cp loramap-logrotate $LOGROTATE/loramap
echo "Copy example-conf to $CONFIG."
cp "logrotate-$DEBNAME" "$LOGROTATE/$DEBNAME.conf"
chmod 644 $LOGROTATE/*
echo "Copy $DEBNAME.conf to $LOGROTATE."
dpkg-deb --build $ROOT
mv $HOMEDIR/deb.deb ../../../Builds/"$ARCHT-loramap_$VMAJOR.$VMINOR-$VBUILD.deb"
rm $HOMEDIR/deb -r
echo "Build deb packet."
TARGETFILE="$DEBNAME""_$VMAJOR.$VMINOR-$VBUILD.deb"
mv $HOMEDIR/deb.deb "../../../Builds/$ARCHT-$TARGETFILE"
echo "Move $ARCHT-$TARGETFILE to Builds."
rm $HOMEDIR/deb -r
echo "Remove $HOMEDIR/deb."
echo "##[set-output name=debuilderfile;]$TARGETFILE"
echo "##[set-output name=builddaterelease;]$(date +"%F_%H%M%S")"

View File

@ -1,9 +1,17 @@
#!/bin/bash
systemctl enable loramap
DEBNAME="loramap"
systemctl enable $DEBNAME
systemctl daemon-reload
if [ -f /tmp/loramap_service_runner ]; then
service loramap start
rm /tmp/loramap_service_runner
touch /var/log/loramap.log
chown loramapbot:loramapbot /var/log/loramap.log
chmod 644 /var/log/loramap.log
chown -R loramapbot:loramapbot /usr/local/bin/$DEBNAME
if [ -f /tmp/$DEBNAME_service_runner ]; then
service $DEBNAME start
rm /tmp/$DEBNAME_service_runner
fi

View File

@ -1,2 +1,6 @@
#!/bin/bash
useradd -M loramapbot
usermod -L loramapbot
groupadd loramapbot
usermod -G loramapbot,adm loramapbot

View File

@ -1,7 +1,9 @@
#!/bin/bash
if [[ $(systemctl is-active loramap || true) == "active" ]]
DEBNAME="loramap"
if [[ $(systemctl is-active $DEBNAME || true) == "active" ]]
then
touch /tmp/loramap_service_runner
service loramap stop
touch /tmp/$DEBNAME_service_runner
service $DEBNAME stop
fi

View File

@ -5,10 +5,12 @@ Description=Lora-Map
After=network-online.target
[Service]
User=root
Group=root
User=loramapbot
Group=loramapbot
WorkingDirectory=/usr/local/bin/loramap
ExecStart=/usr/bin/mono /usr/local/bin/loramap/Lora-Map.exe
PermissionsStartOnly=true
ExecStartPre=setcap 'cap_net_bind_service=+ep' /usr/share/dotnet/dotnet
ExecStart=/usr/bin/dotnet /usr/local/bin/loramap/Lora-Map.dll
KillMode=control-group
TimeoutStopSec=5
Restart=on-failure

View File

@ -30,54 +30,42 @@
margin-left: 210px;
border-left: 1px solid black;
}
#content #nameeditor .title {
margin-bottom: 20px;
font-weight: bold;
}
#content #nameeditor #nametable {
#content .settingstable {
margin-left: 15px;
border-collapse: collapse;
border-collapse: separate;
}
#content #nameeditor #nametable thead {
#content .settingstable thead {
background-color: #CCCCCC;
background-color: rgba(0,0,0,0.2);
}
#content #nameeditor #nametable thead th {
#content .settingstable thead th {
text-align: left;
}
#content #nameeditor #nametable thead .rowid {
width: 60px;
}
#content #nameeditor #nametable thead .rowicon {
width: 65px;
}
#content #nameeditor #nametable thead .rowedit {
width: 50px;
}
#content #nameeditor #nametable thead .rowname {
width: 250px;
}
#content #nameeditor #nametable tbody tr:nth-child(odd) {
#content .settingstable tbody tr:nth-child(odd) {
background-color: #f39d9d;
background-color: rgba(20,0,250,0.1);
}
#content #nameeditor #nametable tbody tr:nth-child(even) {
#content .settingstable tbody tr:nth-child(even) {
background-color: #9c9eee;
background-color: rgba(250,59,0,0.1);
}
#content #nameeditor #nametable tbody tr:hover {
#content .settingstable tbody tr:hover {
background-color: #e4e1e1;
background-color: rgba(0,0,0,0.1);
}
#content #nameeditor #nametable tfoot {
#content .settingstable tfoot {
background-color: #e4e1e1;
background-color: rgba(0,0,0,0.1);
}
#content #nameeditor .pointer {
#content .pointer {
cursor: pointer;
}
#content #nameeditor #nametable tbody .name {
width: 55px;
#content #nameeditor .title {
margin-bottom: 20px;
font-weight: bold;
}
#iconeditor {
@ -113,4 +101,22 @@
#content #eximport .names textarea {
height: 120px;
width: 90%;
}
#content #settingseditor .title {
margin-bottom: 20px;
font-weight: bold;
}
#content #settingseditor .startloc,
#content #settingseditor .wetterwarnings,
#content #settingseditor .savesettings,
#content #settingseditor .gridradius {
margin-left: 15px;
}
#content #settingseditor .fightdedection,
#content #settingseditor .crowddensity {
margin-left: 15px;
margin-top: 20px;
}

View File

@ -5,17 +5,21 @@
<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>
<div id="menu">
<ul>
<li><a onclick="menu_names(); return false;" href="#">Namen und Icons</a></li>
<li><a onclick="menu_overlay(); return false;" href="#">Karteneditor</a></li>
<li><a onclick="menu_eximport(); return false;" href="#">Ex- und Import</a></li>
<li><a onclick="return AdminMenu.Names();" href="#">Namen und Icons</a></li>
<!-- <li><a onclick="return AdminMenu.Overlay();" href="#">Karteneditor</a></li> -->
<li><a onclick="return AdminMenu.Settings();" href="#">Einstellungen</a></li>
<li><a onclick="return AdminMenu.ExImport();" href="#">Ex- und Import</a></li>
</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,109 +1,38 @@
function menu_names() {
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();
}
function menu_overlay() {
}
function menu_eximport() {
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) {
ExImport.ParseJson(ajaxnames.responseText, ajaxgeo.responseText);
}
};
ajaxgeo.open("GET", "/admin/get_json_geo", true);
ajaxgeo.send();
}
};
ajaxnames.open("GET", "/admin/get_json_names", true);
ajaxnames.send();
}
var NamesEditor = {
var NamesEditor = {
//public variables
iconeditorcounter: 0,
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'>";
html += "<thead><tr><th class='rowid'>ID</th><th class='rowname'>Name</th><th class='rowicon'>Icon</th><th class='rowedit'></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>";
}
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><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>";
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);
},
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>";
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 (query.hasOwnProperty("icon") && query["icon"] === "person") {
if (Object.prototype.hasOwnProperty.call(query, "icon") && query["icon"] === "person") {
markerobj["person"] = {};
if (query.hasOwnProperty("person-org")) {
if (Object.prototype.hasOwnProperty.call(query, "person-org")) {
markerobj["person"]["org"] = query["person-org"];
}
if (query.hasOwnProperty("person-funct")) {
if (Object.prototype.hasOwnProperty.call(query, "person-funct")) {
markerobj["person"]["funct"] = query["person-funct"];
}
if (query.hasOwnProperty("person-rang")) {
if (Object.prototype.hasOwnProperty.call(query, "person-rang")) {
markerobj["person"]["rang"] = query["person-rang"];
}
if (query.hasOwnProperty("person-text")) {
if (Object.prototype.hasOwnProperty.call(query, "person-text")) {
markerobj["person"]["text"] = query["person-text"];
}
if (query.hasOwnProperty("person-typ")) {
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"]) {
@ -117,186 +46,6 @@ var NamesEditor = {
}
return markerobj;
},
Add: function () {
var newrow = document.createElement("tr");
newrow.innerHTML = "<td><input class='name'/></td>";
newrow.innerHTML += "<td><input /></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><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 };
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;
if (el.children[2].children[0].hasAttribute("data")) {
url = el.children[2].children[0].data;
}
el.innerHTML = "<td><input class='name' value='" + id + "'/></td>";
el.innerHTML += "<td><input 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><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;
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><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) {
var html = title + ": ";
var eventtext = "NamesEditor.ChangeLinkPreview(\"" + key + "\",this.selectedOptions);";
if (typeof group !== "undefined") {
eventtext += " document.getElementById(\"" + group + "\"+this.value).style.display = \"block\";'";
}
html += "<select onchange='" + eventtext + "'" + (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);
@ -317,45 +66,239 @@ var NamesEditor = {
}
}
document.getElementById("markerprev").data = cur.file + "?" + this.JoinObjectIntoQuery(query);
}
};
var ExImport = {
ParseJson: function (jsonnames, jsongeo) {
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>";
document.getElementById("content").innerHTML = html;
document.getElementById("ex_names").value = jsonnames;
document.getElementById("ex_geo").value = jsongeo;
},
SaveNames: function () {
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 an names.json gespeichert!");
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(document.getElementById("ex_names").value);
savenames.open("PUT", "/admin/api/json/name", true);
savenames.send(JSON.stringify(namejson));
},
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)!");
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] || "");
}
};
savegeo.open("POST", "/admin/set_json_geo", true);
savegeo.send(document.getElementById("ex_geo").value);
}
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

@ -13,6 +13,7 @@
#bigmap .leaflet-map-pane .leaflet-marker-pane .snumber-icon {
font-weight: bold;
font-size: 5px;
white-space: nowrap;
}
#bigmap .leaflet-map-pane .leaflet-marker-pane .coord-icon {
font-size: 13px;
@ -20,6 +21,28 @@
color: #ffffff;
font-weight: bold;
}
#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor {
background-color: white;
border: 2px solid black;
padding: 5px;
border-radius: 10px;
}
#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor.alert {
border: 2px solid red;
}
#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor span {
display: block;
text-align: center;
}
#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor .name {
font-weight: bold;
}
#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor .wind {
font-size: 18px;
}
#bigmap .leaflet-map-pane .leaflet-marker-pane .mapsensor.alert .wind {
color: red;
}
/* Optional: Makes the sample page fill the window. */
html, body {
@ -59,12 +82,24 @@ object {
#menucollumn .pos {
background-image: url("icons/placeholder.png");
}
#menucollumn .filter {
background-image: url("icons/filter.png");
}
#menucollumn .admin {
background-image: url("icons/admin-with-cogwheels.png");
}
#menucollumn .info {
background-image: url("icons/information.png");
}
#menucollumn .weather {
background-image: url("icons/storm-in.png");
}
#menucollumn .weather.ac {
background-image: url("icons/storm-ac.png");
}
#menucollumn .suche {
background-image: url("icons/search.png");
}
#pannels {
position: absolute;
@ -89,6 +124,10 @@ object {
#pannels #pannels_pos .item {
margin: 4px;
}
#pannels #pannels_pos .item.filter {
opacity: 0.4;
background-color: rgba(1,1,1,0.2);
}
#pannels #pannels_pos .item .color {
float: left;
width: 2px;
@ -101,10 +140,10 @@ object {
margin-right: 5px;
}
#pannels #pannels_pos .item .icon img {
height: 40px;
width: 40px;
margin-left: 3px;
}
height: 40px;
width: 35px;
margin-left: 3px;
}
#pannels #pannels_pos .item .line1 .name {
font-weight: bold;
}
@ -203,6 +242,103 @@ object {
margin-left: 70px;
}
#pannels #pannels_weather {
margin: 5px;
display: none;
}
#pannels #pannels_weather h1 {
margin: 0;
font-size: 20px;
text-align: center;
}
#pannels #pannels_weather .alertitem {
padding: 5px;
margin-bottom: 10px;
}
#pannels #pannels_weather .alertitem.minor {
background-color: #ffeb3b;
}
#pannels #pannels_weather .alertitem.moderate {
background-color: #fb8c00;
}
#pannels #pannels_weather .alertitem.severe {
background-color: #e53935;
}
#pannels #pannels_weather .alertitem.extreme {
background-color: #880e4f;
}
#pannels #pannels_weather .alertitem.hitze {
background-color: #c9f;
}
#pannels #pannels_weather .alertitem .head {
font-weight: bold;
display: block;
font-size: 14px;
}
#pannels #pannels_weather .alertitem .ort {
display: block;
text-align: right;
margin-bottom: 10px;
}
#pannels #pannels_weather .alertitem .text {
display: block;
margin-bottom: 10px;
}
#pannels #pannels_filter {
margin: 5px;
display: none;
}
#pannels #pannels_filter h1 {
font-size: 16px;
}
#pannels #pannels_filter select {
height: 140px;
width: 235px;
}
#pannels #pannels_suche {
display: none;
padding: 5px;
}
#pannels #pannels_suche .searchtitle {
font-weight: bold;
font-size: 13px;
}
#pannels #pannels_suche input {
width: 220px;
margin-top: 5px;
}
#pannels #pannels_suche #search_results {
border-top: 1px solid black;
margin-top: 10px;
padding-top: 10px;
}
#pannels #pannels_suche #search_results .result {
background-color: rgba(0,0,0,0.2);
margin-bottom: 5px;
cursor: pointer;
}
#pannels #pannels_suche #search_results .result .text {
display: inline-block;
width: 200px;
}
#pannels #pannels_suche #search_results .result .text .title {
font-weight: bold;
display: block;
}
#pannels #pannels_suche #search_results .result .text .desc {
display: block;
}
#pannels #pannels_suche #search_results .result .box {
display: inline-block;
height: 25px;
width: 15px;
border-style: solid;
border-width: 2px;
vertical-align: top;
}
#overlays #cameracount {
position: absolute;
top: 10px;
@ -211,12 +347,14 @@ object {
font-size: 11px;
font-family: "Verdana";
padding: 3px;
display: inline-flex;
}
#overlays #cameracount .camera {
background-color: white;
border: rgba(0,0,0,0.3) solid 2px;
border-radius: 5px;
padding: 4px;
margin-right: 5px;
}
#overlays #cameracount .camera span {
display: block;

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -10,15 +10,36 @@
<body>
<div id="bigmap"></div>
<div id="menucollumn">
<span class="pos" onclick="showHidePanel('pannels_pos');"></span>
<span class="admin" onclick="showHidePanel('pannels_admin');"></span>
<span class="info" onclick="showHidePanel('pannels_version');"></span>
<span class="pos" onclick="MenuObject.ShowHidePanel('pannels_pos');"></span>
<span class="filter" onclick="MenuObject.ShowHidePanel('pannels_filter');"></span>
<span class="suche" onclick="MenuObject.ShowHidePanel('pannels_suche');"></span>
<span class="weather" onclick="MenuObject.ShowHidePanel('pannels_weather');" id="menucol_weather_icon"></span>
<span class="admin" onclick="MenuObject.ShowHidePanel('pannels_admin');"></span>
<span class="info" onclick="MenuObject.ShowHidePanel('pannels_version');"></span>
</div>
<div id="pannels">
<div id="pannels_pos"></div>
<div id="pannels_info">
<!-- Shows infos about selected device here, this will be cleand by js -->
</div>
<div id="pannels_filter">
<h1>Angezeigte Gruppen</h1>
<select multiple onchange="MarkerObject.ChangeFilter(this);">
<option value="fw">Feuerwehr</option>
<option value="sani">Rettungsdienst</option>
<option value="pol">Polizei</option>
<option value="oa">Ordnungsamt</option>
<option value="si">Sicherheitsdienst</option>
<option value="thw">Technisches Hilfswerk</option>
<option value="crew">Veranstalter</option>
<option value="dev">Entwickler</option>
</select><br />
<span>(Klicken Sie mit <strong>STRG</strong> in das Formular um mehrere Gruppen auszuwählen)</span><br /><br />
<span>Sind keine Gruppen ausgewählt, werden alle Icons angezeigt, egal welcher Gruppe sie angehören.</span>
</div>
<div id="pannels_weather">
<h1>Keine Gefahren</h1>
</div>
<div id="pannels_admin">
Teste Login...
</div>
@ -34,9 +55,16 @@
<li><a href="https://www.freepik.com/" title="Freepik">Freepik</a> licensed <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></li>
<li><a href="http://www.famfamfam.com/about/" title="Silk Iconset">Silk Iconset</a> licensed <a href="http://creativecommons.org/licenses/by/2.5/" title="Creative Commons BY 2.5" target="_blank">CC 2.5 BY</a></li>
<li><a href="https://www.flaticon.com/authors/those-icons" title="Those Icons">Those Icons</a> licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></li>
<li><a href="https://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></li>
<li><a href="https://www.flaticon.com/authors/dimitry-miroliubov" title="Dimitry Miroliubov">Dimitry Miroliubov</a> from <a href="https://www.flaticon.com/" title="Flaticon">flaticon.com</a></li>
</ul>
</div>
</div>
<div id="pannels_suche">
<span class="searchtitle">Standnummer:</span><br />
<input onkeyup="MenuObject.SearchInGeoJson(this.value);"/>
<div id="search_results"></div>
</div>
</div>
<div id="overlays">
<div id="cameracount"></div>

View File

@ -1,43 +1,93 @@
setInterval(timecorrectionrunner, 60000);
timecorrectionrunner();
function timecorrectionrunner() {
var timecorrection = new XMLHttpRequest();
timecorrection.onreadystatechange = parseAjaxTimecorrection;
timecorrection.open("GET", "/currenttime", true);
timecorrection.send();
}
var timeOffset = 0;
function parseAjaxTimecorrection() {
if (this.readyState === 4 && this.status === 200) {
utcobject = JSON.parse(this.responseText);
if (utcobject.hasOwnProperty("utc")) {
timeOffset = Date.now() - Date.parse(utcobject["utc"]);
var FunctionsObject = {
/// private variables
_internalTimeOffset: 0,
/// public functions
Start: function () {
setInterval(this._QueryJsonEveryMinute, 60000);
setInterval(this._QueryJsonEverySecond, 1000);
this._QueryJsonEveryMinute();
this._QueryJsonEverySecond();
this._QueryJsonStartup();
return this;
},
TimeCalculation: function (timestr, type) {
if (type === "diffraw" || type === "difftext" || type === "difftextn") {
var diff = Math.round((Date.now() - Date.parse(timestr) - this._internalTimeOffset) / 1000);
if (type === "diffraw") {
return diff;
}
if (type === "difftextn" && diff < 0) {
diff = diff * -1;
}
var isneg = false;
if (diff < 0) {
isneg = true;
diff = diff * -1;
}
if (diff < 60) {
return (isneg ? "-" : "") + diff + " s";
}
if (diff < 60 * 60) {
return (isneg ? "-" : "") + Math.floor(diff / 60) + " m";
}
if (diff < 60 * 60 * 24) {
return (isneg ? "-" : "") + Math.floor(diff / (60 * 60)) + " h";
}
return (isneg ? "-" : "") + Math.floor(diff / (60 * 60 * 24)) + " d";
} else if (type === "str") {
var date = new Date(Date.parse(timestr) + this._internalTimeOffset);
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();
}
}
function timeCalculation(timestr, type) {
if (type === "diffraw" || type === "difftext") {
var diff = Math.round((Date.now() - Date.parse(timestr) - timeOffset) / 1000);
if (type === "diffraw") {
return diff;
}
if (diff < 60) {
return diff + " s";
}
if (diff < 60 * 60) {
return Math.floor(diff / 60) + " m";
}
if (diff < 60 * 60 * 24) {
return Math.floor(diff / (60 * 60)) + " h";
}
return Math.floor(diff / (60 * 60 * 24)) + " d";
} else if (type === "str") {
var date = new Date(Date.parse(timestr) + timeOffset);
var str = date.toLocaleString();
return str;
}
}
}.Start();

View File

@ -1,197 +1,280 @@
var mymap = L.map('bigmap').setView(["{%START_LOCATION%}"], 16);
GetMapLayers();
function GetMapLayers() {
var layergetter = new XMLHttpRequest();
layergetter.onreadystatechange = function () {
if (layergetter.readyState === 4 && layergetter.status === 200) {
var maps = JSON.parse(layergetter.responseText);
var i = 0;
for (var key in maps) {
i++;
}
if (i === 1) {
L.tileLayer(maps["online"]["url"], {
attribution: maps["online"]["attribution"],
minZoom: maps["online"]["minZoom"],
maxZoom: maps["online"]["maxZoom"]
}).addTo(mymap);
} else {
var baseMaps = {};
for (key in maps) {
if (key !== "online") {
var basemap = L.tileLayer(maps[key]["url"], {
attribution: maps[key]["attribution"],
minZoom: maps[key]["minZoom"],
maxZoom: maps[key]["maxZoom"],
errorTileUrl: "css/icons/failtile.png"
});
basemap.addTo(mymap);
baseMaps[maps[key]["title"]] = basemap;
break;
}
var MapObject = {
/// public variables
GeoJson: {},
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;
},
/// 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;
}
for (key in maps) {
if (!baseMaps.hasOwnProperty(maps[key]["title"])) {
baseMaps[maps[key]["title"]] = L.tileLayer(maps[key]["url"], {
attribution: maps[key]["attribution"],
minZoom: maps[key]["minZoom"],
maxZoom: maps[key]["maxZoom"],
errorTileUrl: "css/icons/failtile.png"
});
}
}
L.control.layers(baseMaps).addTo(mymap);
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();
}
}
};
layergetter.open("GET", "/getlayer", true);
layergetter.send();
}
var SpecialMarkers = new Array();
GetGeoLayer();
function GetGeoLayer() {
var geogetter = new XMLHttpRequest();
geogetter.onreadystatechange = function () {
if (geogetter.readyState === 4 && geogetter.status === 200) {
var geo = JSON.parse(geogetter.responseText);
if (!(Object.keys(geo).length === 0 && geo.constructor === Object)) {
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);
}
},
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]
})
});
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"]
})
});
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] }) });
},
_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' });
}
}).addTo(mymap);
}
}
}
};
geogetter.open("GET", "/getgeo", true);
geogetter.send();
}
mymap.on('zoomend', function () {
var currentZoom = mymap.getZoom();
if (currentZoom < 14) {
SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "0px";
elem._icon.style.marginLeft = "0px";
elem._icon.style.marginTop = "0px";
},
_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;
for (var key in maps) {
i++;
}
if (i === 1) {
L.tileLayer(maps["online"]["url"], {
attribution: maps["online"]["attribution"],
minZoom: maps["online"]["minZoom"],
maxZoom: maps["online"]["maxZoom"]
}).addTo(this.Map);
} else {
var baseMaps = {};
for (key in maps) {
if (key !== "online") {
var basemap = L.tileLayer(maps[key]["url"], {
attribution: maps[key]["attribution"],
minZoom: maps[key]["minZoom"],
maxZoom: maps[key]["maxZoom"],
errorTileUrl: "css/icons/failtile.png"
});
basemap.addTo(this.Map);
baseMaps[maps[key]["title"]] = basemap;
break;
}
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "0px";
for (key in maps) {
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"],
maxZoom: maps[key]["maxZoom"],
errorTileUrl: "css/icons/failtile.png"
});
}
}
});
} else if (currentZoom === 14) {
SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "0px";
elem._icon.style.marginLeft = "0px";
elem._icon.style.marginTop = "0px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "6px";
}
});
} else if (currentZoom === 15) {
SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "0px";
elem._icon.style.marginLeft = "0px";
elem._icon.style.marginTop = "0px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "9px";
}
});
} else if (currentZoom === 16) {
SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "5px";
elem._icon.style.marginLeft = "-4px";
elem._icon.style.marginTop = "-4px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "13px";
}
});
} else if (currentZoom === 17) {
SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "5px";
elem._icon.style.marginLeft = "-4px";
elem._icon.style.marginTop = "-4px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "16px";
}
});
} else if (currentZoom === 18) {
SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "8px";
elem._icon.style.marginLeft = "-5px";
elem._icon.style.marginTop = "-6px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "25px";
}
});
} else if (currentZoom === 19) {
SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "14px";
elem._icon.style.marginLeft = "-8px";
elem._icon.style.marginTop = "-11px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "45px";
L.control.layers(baseMaps).addTo(this.Map);
}
},
_SetupClickHandler: function () {
this.Map.on("click", this._HidePanel);
},
_SetupMapZoomFontsize: function () {
this.Map.on('zoomend', function () {
var currentZoom = MapObject.Map.getZoom();
if (currentZoom < 14) {
MapObject._SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "0px";
elem._icon.style.marginLeft = "0px";
elem._icon.style.marginTop = "0px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "0px";
}
});
} else if (currentZoom === 14) {
MapObject._SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "0px";
elem._icon.style.marginLeft = "0px";
elem._icon.style.marginTop = "0px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "6px";
}
});
} else if (currentZoom === 15) {
MapObject._SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "0px";
elem._icon.style.marginLeft = "0px";
elem._icon.style.marginTop = "0px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "9px";
}
});
} else if (currentZoom === 16) {
MapObject._SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "5px";
elem._icon.style.marginLeft = "-4px";
elem._icon.style.marginTop = "-4px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "13px";
}
});
} else if (currentZoom === 17) {
MapObject._SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "5px";
elem._icon.style.marginLeft = "-4px";
elem._icon.style.marginTop = "-4px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "16px";
}
});
} else if (currentZoom === 18) {
MapObject._SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "8px";
elem._icon.style.marginLeft = "-5px";
elem._icon.style.marginTop = "-6px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "25px";
}
});
} else if (currentZoom === 19) {
MapObject._SpecialMarkers.forEach(function (elem, index) {
if (elem.feature.properties["description"] === "snumber") {
elem._icon.style.fontSize = "14px";
elem._icon.style.marginLeft = "-8px";
elem._icon.style.marginTop = "-11px";
}
if (elem.feature.properties["description"] === "coord") {
elem._icon.style.fontSize = "45px";
}
});
}
MarkerObject.ScaleSensors("zoom");
});
}
});
mymap.on("click", hidePanel);
function hidePanel(e) {
showHidePanel(null);
}
}.Start();

View File

@ -1,27 +1,65 @@
setInterval(datarunner, 1000);
function datarunner() {
var loc = new XMLHttpRequest();
loc.onreadystatechange = parseAjaxLoc;
loc.open("GET", "/loc", true);
loc.send();
var panic = new XMLHttpRequest();
panic.onreadystatechange = parseAjaxPanic;
panic.open("GET", "/panic", true);
panic.send();
}
var markers = {};
var serverLocation = {};
//https://leafletjs.com/reference-1.4.0.html#marker
function parseAjaxLoc() {
if (this.readyState === 4 && this.status === 200) {
serverLocation = JSON.parse(this.responseText);
for (var key in serverLocation) {
if (serverLocation.hasOwnProperty(key)) {
var positionItem = serverLocation[key];
var MarkerObject = {
/// 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 (Object.prototype.hasOwnProperty.call(this.LocationData, key)) {
var positionItem = this.LocationData[key];
if (positionItem['Latitude'] !== 0 || positionItem['Longitude'] !== 0) {
if (!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'] });
@ -34,67 +72,100 @@ function parseAjaxLoc() {
});
marker = L.marker([positionItem['Latitude'], positionItem['Longitude']], { 'title': positionItem['Name'], 'icon': myIcon });
}
markers[key] = marker.addTo(mymap).on("click", showMarkerInfo, key);
this._Markers[key] = marker.addTo(MapObject.Map).on("click", function () { MenuObject.statusToDevice = this; MenuObject.ShowHidePanel("pannels_info"); }, key);
} else {
markers[key].setLatLng([positionItem['Latitude'], positionItem['Longitude']]);
this._Markers[key].setLatLng([positionItem['Latitude'], positionItem['Longitude']]);
if (positionItem['Icon'] !== null) {
if (markers[key]._icon.children.length === 0) {
markers[key].setIcon(L.divIcon({
if (this._Markers[key]._icon.children.length === 0) {
this._Markers[key].setIcon(L.divIcon({
className: 'pos-marker',
iconSize: [56, 80],
iconAnchor: [0, 80],
html: '<img src="' + positionItem['Icon'] + '" height="80" width="56" />'
}));
} else if (markers[key]._icon.children[0].hasAttribute("src")) {
var old = markers[key]._icon.children[0]["src"].substring(markers[key]._icon.children[0]["src"].indexOf("/", 7) + 1);
} else if (this._Markers[key]._icon.children[0].hasAttribute("src")) {
var old = this._Markers[key]._icon.children[0]["src"].substring(this._Markers[key]._icon.children[0]["src"].indexOf("/", 7) + 1);
if (old !== positionItem['Icon']) {
markers[key]._icon.children[0]["src"] = positionItem['Icon'];
this._Markers[key]._icon.children[0]["src"] = positionItem['Icon'];
}
}
} else {
if (markers[key]._icon.children.length === 1 && markers[key]._icon.children[0].hasAttribute("src")) {
markers[key].removeFrom(mymap);
markers[key] = L.marker([positionItem['Latitude'], positionItem['Longitude']], { 'title': positionItem['Name'] }).addTo(mymap).on("click", showMarkerInfo, key);
if (this._Markers[key]._icon.children.length === 1 && this._Markers[key]._icon.children[0].hasAttribute("src")) {
this._Markers[key].removeFrom(MapObject.Map);
this._Markers[key] = L.marker([positionItem['Latitude'], positionItem['Longitude']], { 'title': positionItem['Name'] }).addTo(MapObject.Map).on("click", function () { MenuObject.statusToDevice = this; MenuObject.ShowHidePanel("pannels_info"); }, key);
}
}
}
var lasttime = timeCalculation(positionItem['Recievedtime'], "diffraw");
if (lasttime <= 5 * 60) {
markers[key]._icon.style.opacity = 1;
} else if (lasttime > 5 * 60 && lasttime <= 15 * 60) {
markers[key]._icon.style.opacity = 0.9 - (lasttime - 5 * 60) / (15 * 60 - 5 * 60) * (0.9 - 0.7);
} else if (lasttime > 15 * 60 && lasttime <= 30 * 60) {
markers[key]._icon.style.opacity = 0.7 - (lasttime - 15 * 60) / (30 * 60 - 15 * 60) * (0.7 - 0.5);
} else if (lasttime > 30 * 60 && lasttime <= 60 * 60) {
markers[key]._icon.style.opacity = 0.5 - (lasttime - 30 * 60) / (30 * 60 - 30 * 60) * (0.5 - 0.25);
} else if (lasttime > 60 * 60) {
markers[key]._icon.style.opacity = 0.25;
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");
if (lasttime <= 5 * 60) {
this._Markers[key]._icon.style.opacity = 1;
} else if (lasttime > 5 * 60 && lasttime <= 15 * 60) {
this._Markers[key]._icon.style.opacity = 0.9 - (lasttime - 5 * 60) / (15 * 60 - 5 * 60) * (0.9 - 0.7);
} else if (lasttime > 15 * 60 && lasttime <= 30 * 60) {
this._Markers[key]._icon.style.opacity = 0.7 - (lasttime - 15 * 60) / (30 * 60 - 15 * 60) * (0.7 - 0.5);
} else if (lasttime > 30 * 60 && lasttime <= 60 * 60) {
this._Markers[key]._icon.style.opacity = 0.5 - (lasttime - 30 * 60) / (30 * 60 - 30 * 60) * (0.5 - 0.25);
} else if (lasttime > 60 * 60) {
this._Markers[key]._icon.style.opacity = 0.25;
}
}
}
}
}
updateStatus();
update_pannels_info();
}
}
var serverPanic = {};
function parseAjaxPanic() {
if (this.readyState === 4 && this.status === 200) {
serverPanic = JSON.parse(this.responseText);
for (var id in serverPanic) {
if (serverPanic.hasOwnProperty(id)) {
var alertItem = serverPanic[id];
if (markers.hasOwnProperty(id)) {
var marker = markers[id];
if (timeCalculation(alertItem["Recievedtime"], "diffraw") <= 10 && marker._icon.className.indexOf(" marker-alert") === -1) {
marker._icon.className += " marker-alert";
showMarkerInfoPerId(id);
} else if (timeCalculation(alertItem["Recievedtime"], "diffraw") > 10 && marker._icon.className.indexOf(" marker-alert") !== -1) {
marker._icon.className = marker._icon.className.replace(" marker-alert", "");
MenuObject.UpdateStatus();
MenuObject._Update_pannels_info();
},
_ParseAJAXPanic: function (serverPanic) {
this.PanicData = serverPanic;
for (var id in this.PanicData) {
if (Object.prototype.hasOwnProperty.call(this.PanicData, id)) {
var alertItem = this.PanicData[id];
if (Object.prototype.hasOwnProperty.call(this._Markers, id)) {
var marker = this._Markers[id];
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);
} else if (FunctionsObject.TimeCalculation(alertItem["Recievedtime"], "diffraw") > 10 && marker._icon.className.indexOf(" marker-alert") !== -1) {
marker._icon.className = marker._icon.className.replace(" marker-alert", "");
}
}
}
}
}
},
_ParseAJAXSensors: function (sensorjson) {
for (var sensorid in sensorjson) {
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 (!Object.prototype.hasOwnProperty.call(this._Sensors, sensorid)) { //Sensor is not drawn until now
var sensor = null;
var sensorIcon = L.divIcon({
className: 'sensoricon',
iconSize: [60, 120],
iconAnchor: [30, 60],
html: '<div class="mapsensor" id="MapSensor_id_' + sensorid + '"><span class="name">' + sensorsettings.Alias + '</span>' +
'<span class="temp">' + sensordata.Temperature + ' °C</span>' +
'<span class="wind">' + sensordata.Windspeed + ' m/s</span>' +
'<span class="hum">' + sensordata.Humidity + ' %rl</span></div>'
});
sensor = L.marker(sensorsettings.Coordinates, { 'title': sensorsettings.Alias, 'icon': sensorIcon, interactive: false });
this._Sensors[sensorid] = sensor.addTo(MapObject.Map);
this.ScaleSensors(document.getElementById('MapSensor_id_' + sensorid));
} else { //Sensor refresh!
document.getElementById('MapSensor_id_' + sensorid).innerHTML = '<span class="name">' + sensorsettings.Alias + '</span>' +
'<span class="temp">' + sensordata.Temperature + ' °C</span>' +
'<span class="wind">' + sensordata.Windspeed + ' m/s</span>' +
'<span class="hum">' + sensordata.Humidity + ' %rl</span>';
}
document.getElementById('MapSensor_id_' + sensorid).className = "mapsensor" + (sensordata.Windspeed > sensorsettings.Level ? ' alert' : '');
}
}
}
}
}
}.Start();

View File

@ -1,175 +1,230 @@
var visiblePanel = null;
function showHidePanel(name) {
if (visiblePanel === null && name !== null) {
document.getElementById("pannels").style.display = "block";
document.getElementById(name).style.display = "block";
visiblePanel = name;
if (typeof window["update_" + name] === "function") {
window["update_" + name]();
}
} else if (visiblePanel === name && name !== "pannels_info" || name === null) {
document.getElementById("pannels").style.display = "none";
if (visiblePanel !== null) {
document.getElementById(visiblePanel).style.display = "none";
}
visiblePanel = null;
} else {
document.getElementById(visiblePanel).style.display = "none";
document.getElementById(name).style.display = "block";
visiblePanel = name;
if (typeof window["update_" + name] === "function") {
window["update_" + name]();
}
}
}
var statusToDevice = null;
function showMarkerInfo(e) {
statusToDevice = this;
showHidePanel("pannels_info");
}
function showMarkerInfoPerId(id) {
statusToDevice = id;
showHidePanel("pannels_info");
}
function showMarkerInfoMenu() {
statusToDevice = this.getAttribute("rel");
showHidePanel("pannels_info");
}
function update_pannels_info() {
document.getElementById("pannels_info").innerHTML = "";
if (serverLocation.hasOwnProperty(statusToDevice)) {
var positionItem = serverLocation[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>";
if (positionItem["Fix"]) {
html += "<div class=\"gps\" style=\"color: green;\">GPS-Empfang</div>";
} else {
html += "<div class=\"gps\" style=\"color: red;\">kein GPS-Empfang</div>";
}
html += "<div class=\"coord\">" + positionItem["UTM"]["Base"] + " <span style=\"color: #b1a831;\">" + positionItem["UTM"]["FieldWidth"] + "</span><span style=\"color: #218c00;\">" + positionItem["UTM"]["Width"] + "</span> <span style=\"color: #b1a831;\">" + positionItem["UTM"]["FieldHeight"] + "</span><span style=\"color: #218c00;\">" + positionItem["UTM"]["Height"] + "</span></div>";
html += "<div class=\"height\"><span class=\"bold\">Höhe:</span> " + positionItem["Height"].toFixed(1) + " m</div>";
html += "<div class=\"hdop\"><span class=\"bold\">HDOP:</span> " + positionItem["Hdop"].toFixed(1) + "</div>";
html += "<div class=\"lanlot\"><span class=\"bold\">Dezimal:</span> " + positionItem["Latitude"].toFixed(5) + ", " + positionItem["Longitude"].toFixed(5) + "</div>";
html += "<div class=\"lastgps\"><span class=\"bold\">Letzter Wert:</span> Vor: " + timeCalculation(positionItem["Lastgpspostime"], "difftext") + "</div>";
html += "<div class=\"update\"><span class=\"bold\">Update:</span> " + timeCalculation(positionItem["Recievedtime"], "str") + "<br><span class=\"bold\">Vor:</span> " + timeCalculation(positionItem["Recievedtime"], "difftext") + "</div>";
html += "<div><span class=\"bold\">RSSI:</span> " + positionItem["Rssi"] + ", <span class=\"bold\">SNR:</span> " + positionItem["Snr"] + "</div>";
if (serverPanic.hasOwnProperty(statusToDevice)) {
var panicData = serverPanic[statusToDevice];
if (panicData["ButtonPressed"].length > 0) {
html += "<div class='alerts'><span class=\"bold\">Alerts:</span>";
for (var i = 0; i < panicData["ButtonPressed"].length; i++) {
html += "<span class='panicitem'>" + timeCalculation(panicData["ButtonPressed"][i], "str")+" (vor " + timeCalculation(panicData["ButtonPressed"][i],"difftext")+")</span>";
var MenuObject = {
/// public variables
statusToDevice: null,
/// private variables
_overviewStatus: new Array(),
_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>";
}
}
html += "</div>";
}
}
document.getElementById("pannels_info").innerHTML = html;
}
}
var overviewStatus = new Array();
function updateStatus() {
for (var id in serverLocation) {
if (serverLocation.hasOwnProperty(id)) {
var positionItem = serverLocation[id];
if (typeof overviewStatus[id] === "undefined") {
overviewStatus[id] = createOverviewElement(positionItem, id);
document.getElementById("pannels_pos").appendChild(overviewStatus[id]);
document.getElementById("search_results").innerHTML = html;
},
ShowHidePanel: function (name) {
if (this._visiblePanel === null && name !== null) {
document.getElementById("pannels").style.display = "block";
document.getElementById(name).style.display = "block";
this._visiblePanel = name;
if (typeof MenuObject["_Update_" + name] === "function") {
MenuObject["_Update_" + name]();
}
} else if (this._visiblePanel === name && name !== "pannels_info" || name === null) {
document.getElementById("pannels").style.display = "none";
if (this._visiblePanel !== null) {
document.getElementById(this._visiblePanel).style.display = "none";
}
this._visiblePanel = null;
} else {
document.getElementById(this._visiblePanel).style.display = "none";
document.getElementById(name).style.display = "block";
this._visiblePanel = name;
if (typeof MenuObject["_Update_" + name] === "function") {
MenuObject["_Update_" + name]();
}
updateOverviewElement(positionItem, id);
}
}
}
function updateOverviewElement(positionItem, id) {
if (positionItem["Batterysimple"] === 0) {
document.getElementById("overview-color-id-" + id).style.backgroundColor = "red";
} else if (positionItem["Batterysimple"] === 1 || positionItem["Batterysimple"] === 2) {
document.getElementById("overview-color-id-" + id).style.backgroundColor = "yellow";
} else if (positionItem["Batterysimple"] === 3 || positionItem["Batterysimple"] === 4) {
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 (positionItem["Fix"]) {
document.getElementById("overview-gps-id-" + id).innerText = "GPS-Empfang";
document.getElementById("overview-gps-id-" + id).style.color = "green";
} else {
document.getElementById("overview-gps-id-" + id).innerText = "kein GPS-Empfang";
document.getElementById("overview-gps-id-" + id).style.color = "red";
}
document.getElementById("overview-update-id-" + id).innerText = "Letzte Werte: vor " + timeCalculation(positionItem["Recievedtime"], "difftext");
if (positionItem['Icon'] === null) {
var icon = document.getElementById("overview-icon-id-" + id);
if (icon.children[0].hasAttribute("rel")) {
document.getElementById("overview-icon-id-" + id).innerHTML = "<img src =\"icons/marker/map-marker.png\">";
},
ShowMarkerInfoPerId: function (id) {
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 (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 && 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";
}
} else {
if (this._overviewStatus[id].className.indexOf("filter") !== -1) {
this._overviewStatus[id].className = "item";
}
}
this._UpdateOverviewElement(positionItem, id);
}
}
},
/// private functions
_UpdateOverviewElement: function (positionItem, id) {
if (positionItem["Batterysimple"] === 0) {
document.getElementById("overview-color-id-" + id).style.backgroundColor = "red";
} else if (positionItem["Batterysimple"] === 1 || positionItem["Batterysimple"] === 2) {
document.getElementById("overview-color-id-" + id).style.backgroundColor = "yellow";
} else if (positionItem["Batterysimple"] === 3 || positionItem["Batterysimple"] === 4) {
document.getElementById("overview-color-id-" + id).style.backgroundColor = "green";
}
} 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";
document.getElementById("overview-name-id-" + id).innerText = positionItem["Name"];
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";
} else {
document.getElementById("overview-gps-id-" + id).innerText = "kein GPS-Empfang";
document.getElementById("overview-gps-id-" + id).style.color = "red";
}
document.getElementById("overview-update-id-" + id).innerText = "Letzte Werte: vor " + FunctionsObject.TimeCalculation(positionItem["Recievedtime"], "difftext");
if (positionItem['Icon'] === null) {
var icon = document.getElementById("overview-icon-id-" + id);
if (icon.children[0].hasAttribute("rel")) {
document.getElementById("overview-icon-id-" + id).innerHTML = "<img src =\"icons/marker/map-marker.png\">";
}
} else {
document.getElementById("overview-icon-id-" + id).innerHTML = "<img src=\"" + positionItem['Icon'] + "&marker-bg=hidden" + "\" rel='svg'/>";
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['MenuIcon']) {
document.getElementById("overview-icon-id-" + id).children[0]["src"] = positionItem['MenuIcon'];
}
} else {
document.getElementById("overview-icon-id-" + id).innerHTML = "<img src=\"" + positionItem['MenuIcon'] + "\" rel='svg'/>";
}
}
},
_CreateOverviewElement: function (positionItem, id) {
var divItem = document.createElement("div");
divItem.className = "item";
divItem.onclick = function showMarkerInfoMenu() {
MenuObject.statusToDevice = this.getAttribute("rel");
MenuObject.ShowHidePanel("pannels_info");
};
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['MenuIcon'] + "\" rel='svg'/></span>";
} else {
divItem.innerHTML += "<span class=\"icon\" id=\"overview-icon-id-" + id + "\"><img src=\"icons/marker/map-marker.png\" /></span>";
}
divItem.innerHTML += "<div class=\"line1\">" +
"<span class=\"name\" id=\"overview-name-id-" + id + "\"></span>" +
"<span class=\"akku\"><img id=\"overview-akkuimg-id-" + id + "\" src=\"icons/akku/" + positionItem["Batterysimple"] + "-4.png\"></span>" +
"</div>";
divItem.innerHTML += "<div class=\"line2\" style=\"color: red;\" id=\"overview-gps-id-" + id + "\">kein GPS-Empfang</div>";
divItem.innerHTML += "<div class=\"line3\" id=\"overview-update-id-" + id + "\">Letzte Werte: vor " + FunctionsObject.TimeCalculation(positionItem["Recievedtime"], "difftext") + "</div>";
return divItem;
},
_ParseAJAXPannelAdmin: function (loggedin) {
if (!loggedin) {
var html = "<h3>Login to Adminpannel</h3><form onsubmit='MenuObject.SubmitLoginForm(); return false;'>";
html += "<div><span class='label'>Username:</span><input id='pannels_admin_name'></div>";
html += "<div><span class='label'>Passwort:</span><input type='password' id='pannels_admin_pass'></div>";
html += "<div><span class='login'><input type='submit'></span></div></form>";
document.getElementById("pannels_admin").innerHTML = html;
} else {
document.getElementById("pannels_admin").innerHTML = "<a href='/admin/' target='_blank'>Adminpannel</a>";
}
},
_Update_pannels_info: function () {
document.getElementById("pannels_info").innerHTML = "";
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>";
if (positionItem["Fix"]) {
html += "<div class=\"gps\" style=\"color: green;\">GPS-Empfang</div>";
} else {
html += "<div class=\"gps\" style=\"color: red;\">kein GPS-Empfang</div>";
}
html += "<div class=\"coord\">" + positionItem["UTM"]["Base"] + " <span style=\"color: #b1a831;\">" + positionItem["UTM"]["FieldWidth"] + "</span><span style=\"color: #218c00;\">" + positionItem["UTM"]["Width"] + "</span> <span style=\"color: #b1a831;\">" + positionItem["UTM"]["FieldHeight"] + "</span><span style=\"color: #218c00;\">" + positionItem["UTM"]["Height"] + "</span></div>";
html += "<div class=\"height\"><span class=\"bold\">Höhe:</span> " + positionItem["Height"].toFixed(1) + " m</div>";
html += "<div class=\"hdop\"><span class=\"bold\">HDOP:</span> " + positionItem["Hdop"].toFixed(1) + "</div>";
html += "<div class=\"lanlot\"><span class=\"bold\">Dezimal:</span> " + positionItem["Latitude"].toFixed(5) + ", " + positionItem["Longitude"].toFixed(5) + "</div>";
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 (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>";
for (var i = 0; i < panicData["ButtonPressed"].length; i++) {
html += "<span class='panicitem'>" + FunctionsObject.TimeCalculation(panicData["ButtonPressed"][i], "str") + " (vor " + FunctionsObject.TimeCalculation(panicData["ButtonPressed"][i], "difftext") + ")</span>";
}
html += "</div>";
}
}
document.getElementById("pannels_info").innerHTML = html;
}
},
_Update_pannels_admin: function () {
var testadmin = new XMLHttpRequest();
testadmin.onreadystatechange = function () {
if (testadmin.readyState === 4 && testadmin.status === 403) {
MenuObject._ParseAJAXPannelAdmin(false);
} else if (testadmin.readyState === 4 && testadmin.status === 200) {
MenuObject._ParseAJAXPannelAdmin(true);
}
};
testadmin.open("GET", "/admin", true);
testadmin.send();
},
_ParseAJAXWeatherAlerts: function (json) {
if (json.length > 0) {
var html = "";
for (var i = 0; i < json.length; i++) {
var walert = json[i];
html += "<div class='alertitem " + walert.Level +" "+ walert.Type + "'>" +
"<span class='head'>" + walert.Headline + "</span>" +
"<span class='ort'>" + walert.Location + "</span>" +
"<span class='text'>" + walert.Body + (walert.Instructions !== "" ? "<br><br>" + walert.Instructions : "") + "</span>" +
"<span class='time'><b>Von:</b> ";
if (FunctionsObject.TimeCalculation(walert.From, "diffraw") < 0) {
html += "in " + FunctionsObject.TimeCalculation(walert.From, "difftextn");
} else {
html += "seit " + FunctionsObject.TimeCalculation(walert.From, "difftext");
}
html += " <b>Bis:</b> in " + FunctionsObject.TimeCalculation(walert.To, "difftextn") + "</span>" +
"</div>";
}
document.getElementById("pannels_weather").innerHTML = html;
document.getElementById("menucol_weather_icon").className = "weather ac";
} else {
document.getElementById("pannels_weather").innerHTML = "<h1>Keine Gefahren</h1>";
document.getElementById("menucol_weather_icon").className = "weather";
}
}
}
function createOverviewElement(positionItem, id) {
var divItem = document.createElement("div");
divItem.className = "item";
divItem.onclick = showMarkerInfoMenu;
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>";
} else {
divItem.innerHTML += "<span class=\"icon\" id=\"overview-icon-id-" + id + "\"><img src=\"icons/marker/map-marker.png\" /></span>";
}
divItem.innerHTML += "<div class=\"line1\">" +
"<span class=\"name\" id=\"overview-name-id-" + id + "\"></span>" +
"<span class=\"akku\"><img id=\"overview-akkuimg-id-" + id + "\" src=\"icons/akku/" + positionItem["Batterysimple"] + "-4.png\"></span>" +
"</div>";
divItem.innerHTML += "<div class=\"line2\" style=\"color: red;\" id=\"overview-gps-id-" + id + "\">kein GPS-Empfang</div>";
divItem.innerHTML += "<div class=\"line3\" id=\"overview-update-id-" + id + "\">Letzte Werte: vor " + timeCalculation(positionItem["Recievedtime"], "difftext") + "</div>";
return divItem;
}
function update_pannels_admin() {
var testadmin = new XMLHttpRequest();
testadmin.onreadystatechange = parseAjaxPannelAdmin;
testadmin.open("GET", "/admin", true);
testadmin.send();
}
function parseAjaxPannelAdmin() {
if (this.readyState === 4 && this.status === 403) {
var html = "<h3>Login to Adminpannel</h3><form onsubmit='submitloginform();return false;'>";
html += "<div><span class='label'>Username:</span><input id='pannels_admin_name'></div>";
html += "<div><span class='label'>Passwort:</span><input type='password' id='pannels_admin_pass'></div>";
html += "<div><span class='login'><input type='submit'></span></div></form>";
document.getElementById("pannels_admin").innerHTML = html;
} else if (this.readyState === 4 && this.status === 200) {
document.getElementById("pannels_admin").innerHTML = "<a href='/admin/' target='_blank'>Adminpannel</a>";
}
}
function submitloginform() {
var adminlogin = new XMLHttpRequest();
adminlogin.onreadystatechange = parseAjaxLogin;
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));
}
function parseAjaxLogin() {
if (this.readyState === 4 && this.status === 200) {
update_pannels_admin();
}
}
}.Start();

View File

@ -1,21 +1,22 @@
setInterval(overlayrunner, 1000);
function overlayrunner() {
var ccount = new XMLHttpRequest();
ccount.onreadystatechange = parseAjaxCount;
ccount.open("GET", "/cameracount", true);
ccount.send();
var cdensity = new XMLHttpRequest();
cdensity.onreadystatechange = parseAjaxDensity;
cdensity.open("GET", "/crowdcount", true);
cdensity.send();
}
function parseAjaxCount() {
if (this.readyState === 4 && this.status === 200) {
var cameracounts = JSON.parse(this.responseText);
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>";
@ -27,23 +28,21 @@ function parseAjaxCount() {
}
}
document.getElementById("cameracount").innerHTML = camerastext;
}
}
function parseAjaxDensity() {
if (this.readyState === 4 && this.status === 200) {
var cameradensy = JSON.parse(this.responseText);
},
_ParseAJAXDensity: function (cameradensy) {
var densystext = "";
for (var densyid in cameradensy) {
if (cameradensy.hasOwnProperty(densyid)) {
var densy = cameradensy[densyid];
var densytext = "<div class='camera'>";
densytext += "<span class='name'>" + densyid + "</span>";
densytext += "<span class='count'>" + densy["DensityCount"] + "</span>";
densytext += "</div>";
densystext += densytext;
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>";
densytext += "<span class='count'>" + densy["DensityCount"] + "</span>";
densytext += "</div>";
densystext += densytext;
}
}
}
document.getElementById("crwoddensy").innerHTML = densystext;
}
}
}.Start();

View File

@ -1,14 +1,45 @@
# Fraunhofer.Fit.IoT.LoraMap (Lora-Map)
Program that displays items with coordinates from Mqtt on a map
## Linking to
### Internal
* BlubbFish.Utils ([Utils](http://git.blubbfish.net/vs_utils/Utils))
* BlubbFish.Utils.IoT ([Utils-IoT](http://git.blubbfish.net/vs_utils/Utils-IoT))
* BlubbFish.Utils.IoT.Bots ([Bot-Utils](http://git.blubbfish.net/vs_utils/Bot-Utils))
* BlubbFish.Utils.IoT.Connector.Data.Mqtt ([ConnectorDataMqtt](http://git.blubbfish.net/vs_utils/ConnectorDataMqtt))
### External
* litjson
* M2Mqtt
* Mono.Posix
# Fraunhofer.Fit.IoT.LoraMap (Lora-Map)
<!-- Short description of the project. -->
Program that displays items with coordinates from Mqtt on a map. This readme is meant for describing the application.
![Picture of the Application](Lora-Map.jpg)
<!-- A teaser figure may be added here. It is best to keep the figure small (<500KB) and in the same repo -->
## Getting Started
<!-- Instruction to make the project up and running. -->
The project documentation is available on the [Wiki](https://github.com/MONICA-Project/lora-map/wiki).
## Deployment
<!-- Deployment/Installation instructions. If this is software library, change this section to "Usage" and give usage examples -->
This repository is only for containing the code from Lora-Map. If you want to develop, please goto the [Map-Project](https://github.com/MONICA-Project/map-project). This repository contains all references as github submodules, even this one.
## Development
<!-- Developer instructions. -->
* Versioning: Use [SemVer](http://semver.org/) and tag the repository with full version string. E.g. `v1.0.0`
### Prerequisite
This projects depends on different librarys.
#### Linking to
##### Internal
* BlubbFish.Utils ([Utils](http://git.blubbfish.net/vs_utils/Utils))
* BlubbFish.Utils.IoT ([Utils-IoT](http://git.blubbfish.net/vs_utils/Utils-IoT))
* BlubbFish.Utils.IoT.Bots ([Bot-Utils](http://git.blubbfish.net/vs_utils/Bot-Utils))
* BlubbFish.Utils.IoT.Connector.Data.Mqtt ([ConnectorDataMqtt](http://git.blubbfish.net/vs_utils/ConnectorDataMqtt))
##### External
* litjson
* M2Mqtt
* CordinateSharp
## Contributing
Contributions are welcome.
Please fork, make your changes, and submit a pull request. For major changes, please open an issue first and discuss it with the other authors.
## Affiliation
![MONICA](https://github.com/MONICA-Project/template/raw/master/monica.png)
This work is supported by the European Commission through the [MONICA H2020 PROJECT](https://www.monica-project.eu) under grant agreement No 732350.

BIN
doc/Alert.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

323
doc/Communcation.yml Normal file
View File

@ -0,0 +1,323 @@
asyncapi: 2.0.0
info:
title: Lora-Internal-Flow
version: '1.0.0'
description: |
Internal Communication for:
* Lora-Gateway
* Lora-Map
license:
name: LGPL3
url: http://www.gnu.org/licenses/lgpl-3.0.html
servers:
production:
url: 127.0.0.1:{port}
protocol: mqtt
description: Lora-Broker
security:
- userPassword: []
variables:
port:
description: Secure connection (TLS) is available through port 8883.
default: '1883'
enum:
- '1883'
- '8883'
defaultContentType: application/json
channels:
lora/data/{deviceID}:
description: Topic witch contains the tracking data.
parameters:
deviceID:
$ref: '#/components/parameters/deviceID'
subscribe:
operationId: loradata
message:
$ref: '#/components/messages/loradata'
lora/panic/{deviceID}:
description: Topic witch contains the tracking data, when the panic buttons was pressed
parameters:
deviceID:
$ref: '#/components/parameters/deviceID'
subscribe:
operationId: lorapanic
message:
$ref: '#/components/messages/loradata'
lora/status/{deviceID}:
description: Topic witch contains status of the devices
parameters:
deviceID:
$ref: '#/components/parameters/deviceID'
subscribe:
operationId: lorastatus
message:
$ref: '#/components/messages/lorastatus'
components:
messages:
loradata:
name: trackerData
title: Positionsdata
summary: Informs you about a Position and Status of a Tracker
contentType: application/json
payload:
$ref: "#/components/schemas/loradataPayload"
lorastatus:
name: loraStatus
title: Statusdata
summary: Informs you about a Status of a Tracker
payload:
$ref: "#/components/schemas/lorastausPayload"
schemas:
loradataPayload:
$ref: "#/components/schemas/loradataObject"
lorastausPayload:
$ref: "#/components/schemas/lorastatusObject"
lorastatusObject:
type: object
properties:
Bandwidth:
type: integer
description: Bandwidth on witch the Signal was recieved
minimum: 7800
maximum: 500000
BatteryLevel:
type: number
description: Voltage of the battery from the device
minimum: 2.5
maximum: 5
Calculatedcrc:
type: integer
description: The calculated CRC
Codingrate:
type: integer
description: The Codingrate in witch the Signal was recieved
minimum: 5
maximum: 8
Crcstatus:
type: string
description: Shows the CRC-Status in a Field
enum:
- Ok
- Bad
- No
DeviceStatus:
type: string
description: Shows the internal state in a Field
enum:
- Startup
- Powersave
- Shutdown
Frequency:
type: integer
description: The Frequency on that the Message was arrived
FrequencyOffset:
type: integer
description: The internal offset to the base frequency, to compensate cheap china rf modules
Host:
type: string
description: Name of the Gateway that Recieves the Data
IpAddress:
type: string
description: IP-Address of the device, for debug
default: "0.0.0.0"
format: "[0-9]\\.[0-9]\\.[0-9]\\.[0-9]"
Name:
type: string
description: Name of the GPS-Tracker, must be unique between every Device
format: "/[a-z]{2}/i"
PacketRssi:
type: number
description: Recieve Signal Strength Index for the whole LORA-Messgae
Receivedtime:
type: string
description: Timestamp of the Gateway, when it recieves the LORA-Message
format: dd/mm/YYYY hh:MM:ss
default: 01/01/2019 12:00:00
Recieverinterface:
type: integer
description: Internal virtual Radio of the Gateway, witch recieves the LORA-Messange
Recieverradio:
type: integer
description: Internal Radio of the Gateway, witch recieves the LORA-Messange
Rssi:
type: number
description: Recieve Signal Strength Index for the LORA-Message
Snr:
type: number
description: Signal to Noise Ratio of the LORA-Message
Snrmax:
type: number
description: Maximum Signal to Noise Ratio of the LORA-Message
Snrmin:
type: number
description: Minimum Signal to Noise Ratio of the LORA-Message
Spreadingfactor:
type: integer
description: The Spreadingfactor of the LORA-Message
minimum: 7
maximum: 12
Time:
type: integer
description: Internal Timecounter of the LORA-Reciever
Version:
type: integer
description: Software-Versionsnumber of the Device
WifiActive:
type: boolean
description: Status if the Device successufly connect to a wifi
WifiSsid:
type: string
description: SSID of the WIFI witch the device connects to.
loradataObject:
type: object
required:
- BatteryLevel
- Gps
- Name
- Receivedtime
- Rssi
- Snr
properties:
Bandwidth:
type: integer
description: Bandwidth on witch the Signal was recieved
minimum: 7800
maximum: 500000
BatteryLevel:
type: number
description: Voltage of the battery from the device
minimum: 2.5
maximum: 5
Calculatedcrc:
type: integer
description: The calculated CRC
Codingrate:
type: integer
description: The Codingrate in witch the Signal was recieved
minimum: 5
maximum: 8
Crcstatus:
type: string
description: Shows the CRC-Status in a Field
enum:
- Ok
- Bad
- No
Frequency:
type: integer
description: The Frequency on that the Message was arrived
Gps:
type: object
description: Gps-Data of a Message
required:
- Fix
- Hdop
- Height
- LastGPSPos
- LastLatitude
- LastLongitude
- Latitude
- Longitude
properties:
Fix:
type: boolean
description: Status of the Tracker, true if it has GPS-Signal
Hdop:
type: number
description: Dislocation from GPS-Reciever
minimum: 0.8
maximum: 25
Height:
type: number
description: Height of the GPS-Reciever
LastGPSPos:
type: string
description: Timestamp when the GPS-Reciever has its last position
format: dd/mm/YYYY hh:MM:ss
default: 01/01/2019 12:00:00
LastLatitude:
type: number
description: Last Latitude of the GPS-Reciever
default: 50.7
LastLongitude:
type: number
description: Last Longitude of the GPS-Reciever
default: 7.2
Latitude:
type: number
description: Latitude of the GPS-Reciever
default: 50.7
Longitude:
type: number
description: Longitude of the GPS-Reciever
default: 7.2
Time:
type: string
description: Timestamp of the GPS-Reciever, that it gets from the Satelites
format: dd/mm/YYYY hh:MM:ss
default: 01/01/2019 12:00:00
Host:
type: string
description: Name of the Gateway that Recieves the Data
Name:
type: string
description: Name of the GPS-Tracker, must be unique between every Device
format: "/[a-z]{2}/i"
PacketRssi:
type: number
description: Recieve Signal Strength Index for the whole LORA-Messgae
Receivedtime:
type: string
description: Timestamp of the Gateway, when it recieves the LORA-Message
format: dd/mm/YYYY hh:MM:ss
default: 01/01/2019 12:00:00
Recieverinterface:
type: integer
description: Internal virtual Radio of the Gateway, witch recieves the LORA-Messange
Recieverradio:
type: integer
description: Internal Radio of the Gateway, witch recieves the LORA-Messange
Rssi:
type: number
description: Recieve Signal Strength Index for the LORA-Message
Snr:
type: number
description: Signal to Noise Ratio of the LORA-Message
Snrmax:
type: number
description: Maximum Signal to Noise Ratio of the LORA-Message
Snrmin:
type: number
description: Minimum Signal to Noise Ratio of the LORA-Message
Spreadingfactor:
type: integer
description: The Spreadingfactor of the LORA-Message
minimum: 7
maximum: 12
Time:
type: integer
description: Internal Timecounter of the LORA-Reciever
securitySchemes:
userPassword:
type: userPassword
description: Using Username and Password to connect to online broker
parameters:
deviceID:
description: The ID of the streetlight.
schema:
type: string

1858
doc/Communication.md Normal file

File diff suppressed because it is too large Load Diff

BIN
doc/Details.pdn Normal file

Binary file not shown.

BIN
doc/Details.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

BIN
doc/Empfang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

BIN
doc/Filter-Select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
doc/Filter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

BIN
doc/Filter2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

BIN
doc/Filter3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

BIN
doc/Gesamtliste.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
doc/Gesamtliste.psd Normal file

Binary file not shown.

BIN
doc/Global.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 KiB

BIN
doc/Global.psd Normal file

Binary file not shown.

BIN
doc/Infos1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

BIN
doc/Infos2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

BIN
doc/Infos3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 KiB

BIN
doc/Infos4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

BIN
doc/Infos5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

BIN
doc/Layer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

BIN
doc/Liste.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 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

BIN
doc/Suche.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

BIN
doc/Symbol-Filter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
doc/Tracker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 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

BIN
doc/Wetter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

BIN
doc/Wettergefahren.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

BIN
doc/Zoombuttons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 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.