Compare commits

...

126 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
3542b87156 Merge branch 'v1.2.8' into 'master'
[1.2.8] Improving the UI

See merge request monica/lora-map/lora-map!1
2019-07-10 14:26:27 +02:00
4f628a5a01 [1.2.8] Improving the UI 2019-07-10 14:14:09 +02:00
137edb6011 #18 history an panikbutton pressed
also add some errorhandling with locks
fixing js minors
2019-07-09 20:41:51 +02:00
18f80904b3 #18 history an panikbutton pressed
c# Part
2019-07-09 15:34:54 +02:00
a1b669cd68 #25 Icons flickering when using ssl 2019-07-09 11:14:32 +02:00
51689abb64 #12 Make icon transparent if there is no data update 2019-07-03 15:53:19 +02:00
7924214b66 Changes to new Mqtt topic for camera density 2019-07-03 14:06:38 +02:00
fbefdd156f Move CoordinateSharp to own Library 2019-06-28 14:07:12 +02:00
c4dbea12b3 #14 show description on map in tooltip on area 2019-06-27 11:22:52 +02:00
7be6245ed3 #13 fixing issue with port when using proxy 2019-06-27 11:07:05 +02:00
1d74703504 Move the Dockerreadmepart also upwards
Start Version 1.2.8
2019-06-24 18:17:23 +02:00
8f412d1b94 [1.2.7] Reorganise a lot of things, add Support for Cameradata 2019-06-24 18:07:53 +02:00
6a871ba280 Move Dockerfile and docker-compose.yml into Project repository 2019-06-24 18:07:13 +02:00
de889459bb Display Crowd-Density Data on Map 2019-06-14 13:02:04 +02:00
a9dd168238 Add support for crowddensity data 2019-06-13 15:21:27 +02:00
9b98bd8660 Rename Adminmodel.cs to AdminModel.cs, cause it sould be Uppercase! 2019-06-11 18:22:46 +02:00
Jannis Warnat
6d28eb4b37 Merge branch 'master' of https://gitlab.fit.fraunhofer.de/monica/lora-map/lora-map 2019-06-11 09:53:36 +02:00
Jannis Warnat
ba5dc9e8f3 added maps and json folders - these may need to be persisted 2019-06-11 09:53:06 +02:00
56de95003e Update README.md 2019-06-10 23:10:55 +02:00
Jannis Warnat
131773d5cb clarifications 2019-06-07 17:52:39 +02:00
Jannis Warnat
c5e7b9e103 added Dockerfile and docker-compose.yml
Build:

docker-compose build

Run (Provide ./Lora-Map/config/requests.conf and ./Lora-Map/config/settings.conf):

docker-compose up -d
2019-06-07 17:37:12 +02:00
9f7e6b4b24 Add support to display camera values that counts people. needs to be on a mqtt toipc camera/counting 2019-06-07 15:06:55 +02:00
3a3b3618bd Add CHANGELOG, CONTRIBUTING.md, LICENSE and README.md 2019-05-30 13:32:19 +02:00
0bc3f6c1e7 [1.2.6] New Types of marker for person 2019-04-29 23:08:46 +02:00
e244aef202 [1.2.5] #10 text Letzer Datenempfang is too long when scrollbar is there and #11 set textsize for every zoomlevel 2019-04-29 16:56:15 +02:00
ba3b56ee8d [1.2.4] Can draw Textmarkers on the Map, use MGRS (UTM) on the Map 2019-04-28 13:37:54 +02:00
b82b071d4d now with MGRS Coordinates 2019-04-26 00:18:05 +02:00
3ad491ccd5 [1.2.3] #9 display polygons and marker on the map 2019-04-24 20:27:27 +02:00
8e9388685e More Icons... 2019-04-22 23:25:29 +02:00
08180734e8 [1.2.2] Bugfix, if only recieve panic packet with gps data, update the marker on the map also 2019-04-22 20:56:26 +02:00
617fbc4dea [1.2.1] #6 Load the map from the Device 2019-04-21 15:29:00 +02:00
418a6e8aad [1.2.0] #4 Possible to Ex and Import Setting 2019-04-15 21:19:29 +02:00
3744613277 [1.1.7] #8 Editor for Names 2019-04-14 17:25:37 +02:00
aff3528ac8 continue with nameseditor 2019-04-13 23:12:05 +02:00
981d4042ea change colors in Marker.svg
continue editor for icons
2019-04-11 23:29:30 +02:00
c1786b4e82 continue iconeditor 2019-04-10 20:15:32 +02:00
c81b02546f continue nameseditor 2019-04-09 18:26:47 +02:00
29b6d3fd27 Continue with nameseditor 2019-04-09 09:21:08 +02:00
e41e6ddabc new battery levels
begin of adminpannel and names editor
2019-04-07 23:07:29 +02:00
925e30e086 Fixing dependencies, missing system.web 2019-04-04 21:23:26 +02:00
37ee169ed1 [1.1.6] #5 Create admin area 2019-04-03 23:57:07 +02:00
645070a3c2 refactoring, so that a function called update_(name of the pannel) will automaticaly executed if the pannel is clicked
make the pannel as overflow auto
style loginpage for admin
2019-04-03 20:55:27 +02:00
81ea11e2fe Update Levels for Battery 2019-04-03 13:39:46 +02:00
6cadaf0bd5 forget the project file 2019-04-02 23:33:45 +02:00
8fb438238d add admin session and some resources
implement login function
2019-04-02 23:32:27 +02:00
b19a32c569 start of adminpannel 2019-04-01 18:15:50 +02:00
4ad9814a84 [1.1.5] Add support for alert button
Rename Panicclient to AlarmItem and Botclient to PositionItem, also use all dates in UTC
functions.js is now responsible for timecalculations, so that the utc times are changed to the local browser time
nav.js is now map.js so its more clear what it did
Server.cs has now a function that gives the utc server time for timecorrection
2019-03-31 12:57:59 +02:00
4444b97d3b add alert border and open device info 2019-03-29 13:46:41 +01:00
52406de2ba show primitive alert on screen when panic button pressed 2019-03-28 17:06:46 +01:00
1f8bb79d43 ask server for panic data 2019-03-28 15:18:33 +01:00
c90a311320 add Panicclient and rewrite Botclient so that is less code
add the possiblity to ask the Server for Panic button presses
remove the version box and put that to the global version-info pannel
2019-03-27 19:36:45 +01:00
c25b6f7ebb add new Icons
not redraw the icons in the devices list
2019-03-27 15:45:11 +01:00
b53f95efec change parsing of names.json
make marker visible in menu.js
2019-03-26 18:43:26 +01:00
6cf17f80d7 remove mapbox
add div icon that use svg directly
2019-03-14 19:32:05 +01:00
2b46f6d16a Update Leaflet to 1.4.0 2019-03-14 19:04:42 +01:00
e9fbc4d13f Change Icons 2019-03-14 15:58:59 +01:00
8e66686c7c Create SVG Icons 2019-03-13 00:18:21 +01:00
aa4f2de809 Modify Icons 2019-03-12 15:49:09 +01:00
cfe818fa00 Change icons 2019-03-11 23:58:08 +01:00
1a4851451e [1.1.4] #3 Create icons for devices 2019-03-10 13:06:15 +01:00
158 changed files with 19367 additions and 11803 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

166
CHANGELOG.md Normal file
View File

@ -0,0 +1,166 @@
# 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
* Implement #18 history an panikbutton pressed
### Bugfixes
* Implement #13 fixing issue with port when using proxy
* Implement #14 show description on map in tooltip on area
* Implement #25 Icons flickering when using ssl
* Add some errorhandling with locks
### Changes
* Move CoordinateSharp to own Library
* Changes to new Mqtt topic for camera density
## 1.2.7
### New Features
* Add support to display camera values that counts people. needs to be on a mqtt toipc camera/counting
* Display Crowd-Density Data on Map
### Bugfixes
* Rename Adminmodel.cs to AdminModel.cs, cause it sould be Uppercase!
* Fix a Parsing Bug in Lora-Map/Model/PositionItem.cs
### Changes
* Move the Dockerfile to the parent project Repository
## 1.2.6
### New Features
* New types of marker for a person, so you can add drawing inside
## 1.2.5
### New Features
* Implement #10 text Letzer Datenempfang is too long when scrollbar is there
### Bugfixes
* Implement #11 set textsize for every zoomlevel
### Changes
* Add an link to kml to geojson converter
## 1.2.4
### New Features
* Possible to draw textmarkers on map (eg. for static text in a polygon)
* Now using MGRS as default output
## 1.2.3
### New Features
* Implement #9 display polygons and marker on the map
### Bugfixes
* change the wort get to post
### Changes
* Default zoomlevel is now 16
## 1.2.2
### Bugfixes
* When only recieve a panic packet with gps data, update also the normal location on the map
## 1.2.1
### New Features
* Implement #6 Load the map from the Device
### Bugfixes
* Show now output 200 of images from Webserver
### Changes
* Now layers.png is also exported
## 1.2.0
### New Features
* Implement #4 Possible to Ex and Import Settings
### Bugfixes
* Move username and password to configfile
### Changes
* Verifiy names.json when sending
* Add logger to Programm
## 1.1.7
### New Features
* Implement #8 Editor for Names and Icons
### Bugfixes
* Fixing missing dependencys of Mono.System.Web in deb packet
* Fixing a Bug when map is not running on port 8080
### Changes
* New Batterylevels
* Change textcolors in Marker.svg
## 1.1.6
### New Features
* new Levels for Battery, so that is ~ 1/5 of time for each Icon
* #5 Create adminpannel
## 1.1.5
### New Features
* Shows a red border on the marker on the map, when the panicbutton is pressed
* Icons are now created by a script from the SVG directly, so all big marker icons are SVGs
* Icons are also now shown in the marker list
* Using Leaflet 1.4.0 now
* Menu with new markers
### Bugfixes
* Times are now complete in UTC internaly and will calculated in the browser to local time.
### Changes
* requests.conf must now have a section `js/map.js` instead of `js/nav.js`
* names.json format has changed
## 1.1.4
### New Features
* Implement #3 Create icons for devices
## 1.1.3
### New Features
* Implement #1 Click on icon and show details
## 1.1.2
### New Features
* Implement #2 Show versions number in Site
## 1.1.1
### New Features
* Add Debian package config
## 1.1.0.0
### New Features
* Change to new JSON format, and make it usable for more than one listener
## 1.0.0.0
### New Features
* First Version, only used as a testoutput for debugging tracker

92
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,92 @@
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Pull Request Process
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
build.
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Increase the version numbers in any examples files and the README.md to the new version that this
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
do not have permission to do that, you may request the second reviewer to merge it for you.
## Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at philip.schell ATTTT fit.fraunhofer.net. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

279
LICENSE Normal file
View File

@ -0,0 +1,279 @@
Tactical Signs based on:
Empfehlungen für Taktische Zeichen im Bevölkerungsschutz from
Bundesamtes für Bevölkerungsschutz und Katastrophenhilfe (BBK).
Icons are redrawn, must not be used for commercial use.
#####################################################################################
Icons are under:
Smashicons (https://www.flaticon.com/authors/smashicons) licensed CC 3.0 BY
Freepik (https://www.freepik.com/) licensed CC 3.0 BY
Silk Iconset (http://www.famfamfam.com/about/) licensed CC 2.5 BY
Those Icons (https://www.flaticon.com/authors/those-icons) licensed by CC 3.0 BY
#####################################################################################
DIN1451M Font:
Copyright (c) (date unknown), Peter Wiegel (https://www.1001fonts.com/alte-din-1451-mittelschrift-font.html),
with Reserved Font Name Alte DIN 1451 Mittelschrift Font Family.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwides
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
######################################################################################
Code is under:
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

BIN
Lora-Map.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,23 +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}") = "Mora-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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoordinateSharp", "..\Librarys\Coordinates\CoordinateSharp\CoordinateSharp.csproj", "{D9D4C842-5818-4E96-9BFE-7ADFB4D811BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -25,43 +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
{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,158 +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="Server.cs" />
<Compile Include="Model\Botclient.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="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\js\leaflet\leaflet-src.esm.js.map" />
<None Include="resources\js\leaflet\leaflet-src.js.map" />
<None Include="resources\js\leaflet\leaflet.js.map" />
</ItemGroup>
<ItemGroup>
<Content Include="resources\css\global.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\css\icons\marker.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\marker\map-marker.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\js\leaflet\images\layers-2x.png" />
<Content Include="resources\js\leaflet\images\layers.png" />
<Content Include="resources\js\leaflet\images\marker-icon-2x.png" />
<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.esm.js" />
<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\nav.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\css\global.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="resources\favicon.ico">
<None Update="resources\admin\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\index.html">
</None>
<None Update="resources\admin\js\adminmenu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</None>
<None Update="resources\admin\js\eximport.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\js\nameseditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\js\settings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\admin\login.html">
<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>
<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>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View File

@ -0,0 +1,146 @@
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

@ -0,0 +1,13 @@
using System;
namespace Fraunhofer.Fit.IoT.LoraMap.Model.Admin {
class AdminSession {
public Boolean IsLoggedin { get; internal set; }
public static Int64 GetRandomSessionid() {
Byte[] buf = new Byte[8];
Random rand = new Random();
rand.NextBytes(buf);
return BitConverter.ToInt64(buf, 0);
}
}
}

View File

@ -1,98 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using BlubbFish.Utils;
using LitJson;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
class Botclient {
public Botclient(JsonData json) {
if (json.ContainsKey("Rssi") && json["Rssi"].IsDouble) {
this.Rssi = (Double)json["Rssi"];
}
if (json.ContainsKey("Snr") && json["Snr"].IsDouble) {
this.Snr = (Double)json["Snr"];
}
if (json.ContainsKey("Receivedtime") && json["Receivedtime"].IsString) {
if (DateTime.TryParse((String)json["Receivedtime"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
this.Upatedtime = updatetime;
}
}
if (json.ContainsKey("BatteryLevel") && json["BatteryLevel"].IsDouble) {
this.Battery = Math.Round((Double)json["BatteryLevel"], 2);
if(this.Battery < 3) {
this.Batterysimple = 0;
} else if(this.Battery < 3.2) {
this.Batterysimple = 1;
} else if(this.Battery < 3.5) {
this.Batterysimple = 2;
} else if(this.Battery < 3.8) {
this.Batterysimple = 3;
} else {
this.Batterysimple = 4;
}
}
if (json.ContainsKey("Gps") && json["Gps"].IsObject) {
if (json["Gps"].ContainsKey("Latitude") && json["Gps"]["Latitude"].IsDouble) {
this.Latitude = (Double)json["Gps"]["Latitude"];
}
if (json["Gps"].ContainsKey("Longitude") && json["Gps"]["Longitude"].IsDouble) {
this.Longitude = (Double)json["Gps"]["Longitude"];
}
if (json["Gps"].ContainsKey("Fix") && json["Gps"]["Fix"].IsBoolean) {
this.Fix = (Boolean)json["Gps"]["Fix"];
}
if (json["Gps"].ContainsKey("LastLatitude") && json["Gps"]["LastLatitude"].IsDouble && !this.Fix) {
this.Latitude = (Double)json["Gps"]["LastLatitude"];
}
if (json["Gps"].ContainsKey("LastLongitude") && json["Gps"]["LastLongitude"].IsDouble && !this.Fix) {
this.Longitude = (Double)json["Gps"]["LastLongitude"];
}
if (json["Gps"].ContainsKey("Hdop") && json["Gps"]["Hdop"].IsDouble) {
this.Hdop = (Double)json["Gps"]["Hdop"];
}
if (json["Gps"].ContainsKey("Height") && json["Gps"]["Height"].IsDouble) {
this.Height = (Double)json["Gps"]["Height"];
}
}
}
public Double Rssi { get; private set; }
public Double Snr { get; private set; }
public DateTime Upatedtime { get; private set; }
public Double Latitude { get; private set; }
public Double Longitude { get; private set; }
public Double Hdop { 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 virtual Dictionary<String, Object> ToDictionary() {
Dictionary<String, Object> dictionary = new Dictionary<String, Object>();
foreach (PropertyInfo item in this.GetType().GetProperties()) {
if (item.CanRead && item.GetValue(this) != null) {
if (item.GetValue(this).GetType().GetMethod("ToDictionary") != null) {
dictionary.Add(item.Name, item.GetValue(this).GetType().GetMethod("ToDictionary").Invoke(item.GetValue(this), null));
} else if (item.GetValue(this).GetType().HasInterface(typeof(IDictionary))) {
Dictionary<String, Object> subdict = new Dictionary<String, Object>();
foreach (DictionaryEntry subitem in (IDictionary)item.GetValue(this)) {
if (subitem.Value.GetType().GetMethod("ToDictionary") != null) {
subdict.Add(subitem.Key.ToString(), subitem.Value.GetType().GetMethod("ToDictionary").Invoke(subitem.Value, null));
}
}
dictionary.Add(item.Name, subdict);
} else if (item.GetValue(this).GetType().BaseType == typeof(Enum)) {
dictionary.Add(item.Name, Helper.GetEnumDescription((Enum)item.GetValue(this)));
} else {
dictionary.Add(item.Name, item.GetValue(this));
}
}
}
return dictionary;
}
}
}

View File

@ -0,0 +1,39 @@
using LitJson;
using System;
using System.Globalization;
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 CameraCounter(JsonData json) => this.Update(json);
internal static String GetId(JsonData json) => (String)json["camera_id"];
internal static Boolean CheckJson(JsonData json) => json.ContainsKey("camera_id") && json["camera_id"].IsString
&& json.ContainsKey("count") && json["count"].IsString
&& json.ContainsKey("name") && json["name"].IsString
&& json.ContainsKey("timestamp") && json["timestamp"].IsString;
internal void Update(JsonData json)
{
if(Int32.TryParse((String)json["count"], out Int32 count)) {
if((String)json["name"] == "total") {
this.Total = count;
} else if((String)json["name"] == "incoming") {
this.Incoming = count;
} else if((String)json["name"] == "outgoing") {
this.Outgoing = count * -1;
}
}
if (DateTime.TryParse((String)json["timestamp"], DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out DateTime updatetime)) {
this.Lastcameradata = updatetime.ToUniversalTime();
}
this.Name = (String)json["name"];
}
}
}

View File

@ -0,0 +1,49 @@
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

@ -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

@ -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,d09GRgABAAAAAC2UAA0AAAAAQoQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAtfAAAABUAAAAc1BgWEkdERUYAAC1cAAAAHQAAAB4AJwCoT1MvMgAAAZgAAAAzAAAAVhQYMm1jbWFwAAADmAAAAXwAAAIKS0qCl2dhc3AAAC1UAAAACAAAAAj//wADZ2x5ZgAABlwAACTqAAA3PEjM+G9oZWFkAAABMAAAA" +
"CkAAAA2ZvZKqGhoZWEAAAFcAAAAIAAAACQPWgX3aG10eAAAAcwAAAHMAAACiHnVMb1sb2NhAAAFFAAAAUYAAAFG8KHj6G1heHAAAAF8AAAAGgAAACAAqABqbmFtZQAAK0gAAADUAAAB48UOolxwb3N0AAAsHAAAATUAAAGUFigoJnicY2BkAAO7BpH/8fw2Xxm4ORjQwf8n/3TYv7DXA5kcDEwgEQDaEggdAAAAeJxjYGRgYK//p8PAwMHw/8l/DfYvDEARFLAIAI0JBmJ4nGNgZGBgWMQQycDKAAIgngADEgAAG1oBEwAAeJxjYGQOY5zAwMqAAzBxszGzMjM" +
"xMbFglXZgUFBUYq//p8PAwF7PeAUowggSBgCK/ATIAHicNZFPSFRRFMZ/797znkMrcRFRIwSZRKER1NRYQjYVydCIklNJm5lSxIWEbaKIKIga6N9Ggqa91LY2QQVtWrVwZ0EbsVZBtLBIiMbv3pwHH9+553z33POdlwNj40u+g+vkrhvnnF8iJ3RZLzPpXsZUe+SK3BD2uEXK9pF6yPkhZmPtOV3Sj7iX3BOXhLywVTjtVxgVzwj7Qhz0AbZCLfSxt9T8KwazKUbsL86Wadqc3t0UuWllYTnO0dR8Tf8Za9cyndOr/+v2boPD/SfkZSzobkln2QJbbLj1w2h9l" +
"aeSfMy79+wW91mVo9ImyS+6/Scqqjf8A4qBla9KM6T4kuZoRK+drdVQ0yyNbJH5kA+6qJfO53V/ic2xdzn0av1OP5CFnOJV9T7hzlNIKpwVtplnMLnMAb/AsP9HxX/jkF3kcKj7afZnY0xqv71+lgF5qbg3nIy52+xy19lpFxhwN+lxT+mJHp5R9l84khToTtaUe80xd58dvk5/+pBq2hTWtPNJjusfWFrTfurU24g7zFGMuytzR94O2k+y6PGK9hP86pxNMxcxwZnAfjsFm6DU8Yf+5DFTbbgXnHLzjAa07/g+Su244xrj6wZKjbN4nGNgYGBmgGAZBkYgycD" +
"IAuQxgvksDC+AtBmDApAlxlDHsJxhP8NRhlMM1xnuMNxneMLwnuE7wx+Gf4xBjAmMk5iOMd1REFGQUpBTUFIwULBUVFKS/P8fqF+BYSHDSoYjDCcYrsH1fUPTJ6wgoSCjoIDQ9//x/4P/D/zf83/X//X/V/9f+X/p/9n/p/6f+H/Cf9v/Bv/+/z3zd/uDxgd1D2of1DzIf5B+v/5eHMT95ABGNga4ZkYmIMGErgAYRCysbOwcnFzcPLx8/AKCQsIiomLiEpJS0jKycvIKikrKKqpq6hqaWto6unr6BoZGxiamZuYWllbWNrZ29g6OTs4urm7uHp5e3j6+fv4Bg" +
"UHBIaFh4RGRUdExsXHxCYkMDY0MzQyt7UDLOhH2doGIbhDRO6cgOSVjXllHXubCErBcIQNDWimYlVUFJOqTchgmMjBkV4OF+hmmTJ05a9r0HjBvARDPLZ8xez5DCwNDE7LHJk3OB5IVQAwA3UV6TwAAAB4AHgAeAB4ANABIAIAAygEWAWgBdgGSAa4B0AHoAfQCAgIOAh4CTAJeAoYCwgLeAxIDSANeA6ID2APqA/wEEAQkBDgEagTSBPgFMAVsBZoFsgXIBgoGIgYwBkwGbAZ8BpwGtgcCBy4HgAewB/AIBAgkCDoIXgh+CJYIrAi+CM4I4Aj2CQIJEglWCYo" +
"JsgnoChYKMgqICqgKvArWCvQLCAs6C1oLngvUDAgMJAxcDHoMmgywDNIM8A0QDSYNdA2CDc4OCA4cDlIOqA7wDxwPMA+4ECYQbBCIEJgQphEOERwRVBF0EaIR7BH8EiISQBJcEooSnBLiEv4TLBNuE9YUEhQ+FHwUyhU4FaQV/BYeFkoWgBa+Ft4XLhdoF5QXtBf+GGwYthkyGWYZdBmCGZAZnhmqGb4Z0hnkGigaihqqGvobdBueAAB4nJV7CUBTV/b3O/e9JO4aQgjKGgIJ+xZCWMIiArILiKCiCIgIiCK4geCuILbWVkWs1Iq4b53qWKU6trbFtmpd4m47O" +
"F2m/dd2utpNIDffve8lip2ZTwd4yUveXc76O+eee2EQw5nN3Gei1xmWGcwwINM6AKtkVaAF2H6du/76XNPt6vvIphPJ0EC0xLTK9Bsk4NOioz3jGMQ0mc2wnu8rYRgZqxwMOq38ZgaIM18xwkwuufck3nr1KkN+EDORGwvmR22lWv534raaNvJ9OW6lF99ugNnMXuPbuVB6VKwDqFiVTKXT8peW5S+5ir9QpN95v/rY87E7Yv520e/MxZj2mA/p/QW/v12MQfGgwXdNp+EtHAdvNYGmSbjDcU34bhOOo3SxjI3Zjm0Vp5HZ1Iwfw8SAMyik/qALCdUTZuwUErV" +
"G6gxyW7FErtKJ1SAdRj7YyaTRpInaZkPIyqKzu9/eU/bmzoVFq84du3ygbMGnMMEwMebM1OT4N1oNmQPhQ7+r24/ek2/YwDa15Q7CaZrPXj30tX13N1ShPmgbWF6eNBX/gDPt6iYnVFGaREyF+QF7VnSGGcgoGA8mgNESSchttcFREKJykxDStMF2TmArVrmp4YlP+ketWKJBdDwtNTVtwcLU5EWLklMXYh/r3QL6/Uz01yZTBkRUlZRUlXz23KzCwlnPVRYUcHaW22nT7vOPek7y2qayyjY/EGkJXYMYLyaY0GSr4KWgchODZhio3BCVmr+FJLmtBKJBrxgGE" +
"tlAEBOhaYNDYX/ljPTsqOgs1NKU/+GCy/jFRl388c2bjj8AvW+k/SllYdRveBBuv75z9phtXN2kIvVUfXh2dm/6pvh8GHJ8xReJkRtSVn/44eobNXhdWF1cctahWPx38IW98w7MbzUwwGzgkqCQtx9GptNLNxi5JGMr3kroB2aJ2R/ZkGdDiZBVakKtjV4rRnJbG2ST3bFzZ0f21LKya8fh1PVrcPr4u3javX/gQtrPx+wPZ4V+Mq2djdwWSVShNroQ5JO9s6NjZ3Z+ebno9eM44dp1nHj8XWj/xz3YSfulstmomfSTkg86iUav0SuITekVEoVEIwe/3dEHD0b" +
"vrt9F33ax2R8UFJSVFxS8/35BQXlZQcEHDE9zMfHPOdxg3juJxyjlSqlKqtQh+3Y4gVPacQrKgZM4eQdOgRM7KP/mMihkrlD+FVKtfINxK8y8Sr6fA5fQbfSqIBelfA4qh0tGozDHBuLH1j462geo1wLxjQeojeibfk/syQbut2PFUNGFnlDBp8cRO32OPB/KOFDqeEuQS3lFe0gFI5DydolOVU+ZOnfu1CnVppa+jp0m086OPuhAs8oXLiw3tVQsWFCAVp3/5JPzpiXnP/5YoGmtuQ95E9mJia6kEp1eK4WJV69e47jbW++19qTxbcYQrFhJ5ldQusFqixJVN" +
"FjsTykfgyKUtpVTJ9RJjPDjnhMP8rAP8oUudN47UZm2qrr46INfj2yuNB3n+XUk/Mwn43lSfREm+o9nQyGAAgFH+NFoncHyHCqSyuqW5sUtlBqHfPPKe9/GTZ6SaWZObf7OyehUPDmvcc0UtLFzweRJiydO3PZ/2LwjZ0dE5OGJH/389snU/PrK2fV03lGED2pfwxhGryOCVlJwlKsI4IyCr+9jGTQYFyww3j7H2fb+C3bjKefu3OH5Tyf0UvxVUkSllkFFrhDQivqju0ZrZ2VDUsYOMJ0pWTO+2dOg9wnw65i9HvcojB6HXw7JXlE3Br3C2hrxycxYn9Evv7F" +
"pgyG8+LWPDl6bWddeR/ScZf6ebRW9xsj4mQiBWjKRgkqYKFej1oV4uEnEFHr0ZC6NuhnW3seXOP/Ra2dVNDbualu6FHZNMkRdS0qIj4pCr7IjTK2youIbfZWzFi68uqYqIqruR33oqsWGCMpTBZHFPsLTAIEnUBElVrBRWPYNfI+XcENhl7HXlfv0GsWjIML/AaKvwYQuP4pHj7gVsyH9gEgsseP15q7v1wIcV2RmrqDXt4dHZ/109OhP2TGH8MM3TuAe4/qCqRs2TC1YzznMrKiYWTprlklzc17E+sLXf/vt9cL1EfNvvPvgQVfJqlUlJatX8/LpIfI5LchnI" +
"CFZZZGL3iInES8ZFS8lhR07Ddd+AyFccNTzZeVrGndvXb4MT82LijImxydEGdhBfT+icllR4a3eytm1tZfXzIkIW/KTPmxlQ3gEmYv3VdRuiaPEW1neY41GuGI04kDiuEjAAGsbigLWNhQL4Cpxe2CKGOBmc/7Uv2Q6kA8EeRE3pO8IOx4ub4JLh/EW3HKY+ngx8zU3h1Va5yMaIVcxN6T3F24IWtHejme2tzNPjqfXDQQyZBGb03eYGwKHYRZUHMbaTTxmRBH9phKdOfDxnWgoBiwKIeZuDRys2AkozTBF5nZCO9+QXmX8v1e2Phh92o3wOXONYVKgKWFMTvG" +
"MxV67v7j11yWTzUxg1oLHuQbBJfEo3i5iLVZhw0+BQK9leUMlnxmVKyO3ZZTBoSQIcOSDdASj5F+J7lTUfYgCGaI/NGp6+prTBUkvvYl0I5t22wbkncFXH+BufBrGg+vJXyc2zcTbbuNTOA06IRHUsA//ZeULdpx3VfnyXTDs77vHoKKcWeBbm1CNvwBRmen7aJ9I8Nh0HsR4z1d/4NeKpu7phF2QAnHQhvfjB9iMu1YFZZ9cfhFKd1B+EMNwn4qOEQ3YMAzxPpIRKVmZ0p/VDGMRsj2BbL8medr8N2Cvv07jqVR5a7SiYz0ZkIxPoiFNLy9cXFa5onazkO+0k" +
"XjSSvxrOGNPrVUhVQoeIudhgwxMvqAKeXTThnQ3P5m4rHbhT7tD8WHIqZ83bwM+C7GL5s8H0dFPDk/eG845nqs68ylqwslrcnMbUSkuWJyXs0TAcZLLcDVEF+7kA5UzAU9/kI4I1VNQ5RQqf6AQyydVCgqqbPN9H/zV6cP3te6Oju7a+4evwHC3+w6byjNqlD4uLj7KmoxO2PLR7T2FsVtBApKtsdP2m4/mLI31Dmv89LPGUN8YwQYon7v4mD2K59L1MS4C4UzPRwuNWtUGGHxeCrVXBo3+ogN/BwNPTHSXyX3zDxDmfp3a8eVve9JKn+M+7f1xmX7Zxa4thsW" +
"EJzp2DBl7MD8ywV3hamN3m8JRgakDfUA6G3HROZzDxxVr+4H92z9qzbcdf8608ZG8FhN5eVHvUEptmadKLDQK0Hwkxtc+/v8JbekRlGnEt+/iG/9dcofXpTJWelsF/kRSmm+QvINc7CnTW93dKK4bUINpteio6UU0l2SGfHuoFXIugjbStm6wpIzkGUcwOkuIbTHAoy9pwQdR7lj80vEHuvG915eePbpi+oIW9naf7JNfyxkrDaIhpN8QYUwSFAfSPzK4COWBBPshDvfgGjND5jJJ0Q+md/peQB/iX3D8o/68zEVCf6Kdbl4tvS5XH41fK9BFng8ELWFTRaFb2" +
"maEDnjZ2M3hK534ioj06UthT/SMY9ebxqAzffMej/+Z1QaIT5I5hM5oE3fPaJpN+1GM7BlHsVKwSatuFSSnZ2QWjVIlSkcIAZtqWmZVq8VErequ4BV76L5O5eio0t0/xCu7m9ftYy3zd+i2oNz902K34of44dbYwj2C0nktx/iGNn72aWOYdyzVvNVXXuLtU0HzON7viWvQOyGdQCqlFGohqb5uWlSh6QsknbB9QtyU+5/i17l/YO/NeaujvXpdRUfd47R/XfgK/sr0gZXfeyIx4deeX8GQ7De0P0NW+xVprKxL+4uE+KqCtTu16qDtn3gsgGOdow9VPjZ2XiY" +
"K53cAbW5tSvz2EWuCUf+0KSyxv8kLUvmuI3zbI5x4n+d95J95tyAGKIHnPqOhISV6uulDFHF6dtSE1tWQTQzwOv6S+xdOXDe1Su/XKxMdLdS1F6+/62eqQ3OoQ/M5JZcs+DNdW/LpB2FVY81OFBJeHoIwFEKC6eq4MfvWgqZtbwXnhp95/+HnTd+X1Gd2Fpa+UFjllK376D38zTf45Om9cZlz8w+PFA8ZMOqzHad+zI07ERxdVhZdKRUNFo/8reX8PYv/mVm67pbwvqyjyAObcDXbi6uB+66rq1cm2PMKQud6Ya1CV+e2j1yVABCzovvQ0qWHuvG1O3fxdfgX2" +
"9U3oXXHjlb2UJ/hn/hbkAvzEJwX5qEZIq0gfIld7kMaB2n3sQt1h0PcBB4v7M1m8SY+BtG2Mr41eaXuB2vwpm++5MBhBzhwX36DN3Xi+xz+xuJMFoca1fsVpdn8PZmz0IJTJO0iTsz/saX4HXz2IVnpF/SQId16cAG8DcjZ9Dn6Fr2PJfDQFGEiNJv/Sfq7CrmmSMp3Bim8jFO/hlDQ3ScrKpTHTiaqbOrbazpgWW/8nwXHdUCFya8wtpjWsM2mMrQVOjld197ec3S91ULWmiMEeVDMJxgqb4H69967Jl7Z1PN8Ey9zb/MPqF30N4qbVGZIhD3a4BOp6FKPln/" +
"uR9ad+61jqEg0UOp+eBfq8ZpU0fymh0ubaJtYFMu5CeskmpkTIcTC66vgL/vwCXwCxbJz+15EXSYDPx6YbTgw1wnrPEI8x3w/cyb53ol7Ce4JdBCQlYEefwWDT5AvffBN6iMZ5h/YOs6RcWa8CaU6lZjk/Wohv9UrdaESktdKNKGKUIWdQsqSxwqS5Grt9KHoL5xTRHTJ4YmF48d3wEi4mhQYYN8+/7Mz86rRhQfHkL/vjOUhgaIpofajuIkTm1dOnIjmrONGyH2XtnEobdwZUzjMThPVhai9kFJJ6GgmsYT6qlzIXLRSK1hYFzwauczNUmkgSNNsjFiRNW2dH" +
"8UNv3XT2isvbYqI2LQpMpKGjO/18ekRYS1vnW3VR6bWVrLJ16pqrl1YvOgCkUc4mWcNN5LMw4CCX0hJR7jzvqpWuUpHKIPRt9viTy88jrtBdXzh6fhtE+vxzzCsAQLXZcz4FZzA6ffSjOZKfAa/xWPME3SLdGIB7WixRGCAcAL96Ea/zt5RuM7X19nZ13ddYebKCCOevjkycvNLhPLK2tRIfevZt1rCItLj9ciGhKXkC4sWX7hWU3WNnyuXzHWE0C6jeQ8o6Zj6fjxwci/gAYc9Ylr8XOWK6a3p75S34xsgbz0EjGkxnBAtnjK1Hk4uX16yYnL6FWJNAV9/d35" +
"BQwORy2izGaUSPmwEe9QSDiJBrnJzArJalR4sL29f3LQwn6To7Jz25peau3Kb2/s2EppK8Dh2J6HJlQljGOWf+VfoLAmBRs/jI/yXAIiuzt5R2qzyUyr9VM2lW+cb8Q9HS84mdKjLDWVr8IyhjvLsmKgsO4ehQx3ssqJisuWOQ2fVZvlFrLxzZ2Wk7/j6qr5B39wrzFihDpuPqpdkqxxDQhxV2UuWZLs76HQO7tkCDjYTH6e6klnyDqGKobIWN5qNXNGRus+MC/ImL+DNqL7zdVMGquArGVus6zNrnZOO8Gh9xg3r/Zn7VKi5IPMvOB/mk3aDLBkIiXc+tOggJ" +
"c13GQ+tzoqcb6zmlvZWr9x5TvQ27cXTxtn3z4kIZClBQomCDT14G7SSiPSv3TRjWQBf4wrTWvjyNL5l5Qt+4Odj9HQ2XnHERTLGTqnZ2sOd6M1P6qriccssjiG6EjxMzvOv+JMQHt20GLnph2YvD6u/RESykIjkKn0hnFbVZ4Q39ZfMIwk9kjFvo4/n+G8ypmM9IeJ+Nq5gfAjqhTxOGqw4QI3Gg0/v3SyJBnWDaMgNMfi0Lq7d6mMIIXdbaxe3kjvUpHDUV8/TOcvlzrp51XpHBZLs2x0XkJsbELd738HdcYE5OYFxu/dXT/FOS/OeUlUz1SstzWsK9Wucz/M" +
"hFyowWnnon8mQyqzlBkJEs3H2jsdwlLUi4hLBok0Ek3C+6NjsOgpHZ9/eQuAoLsz0PSq7UFt78Vp1tWAvxIfyeR+yJV5E8pT/4EPQbyp0ddn6xxDy6hyCIAQ9XiIogvORzWKCIVvePksxpG52TwYqM1ZXX7tYW3vBopsEMs/wx7qhsPdILzUzW+MT05fwesk8rp1ZY3qR16kdrUWRfu6P8vyQAPj3JMcFhHWmq93GtW9Pmr+sc8XBe3vWvpu+Inp1TklGxLjDO5/vOY5Nh19cMyYuf7B00JbGVzvSY7d6h44NdYsQDxl8tP7lvZbYaAYsSqJYJBPKZVab1hFoa" +
"jFu3ZqSMXv2hjJuKETic+2m3Rldq+tRVbtgfwSPuaHU/p7IdVQ6WrghTrFgct4CI+H2UP1nkI+O8SXKCjSr9+c5i0+SKPobkVEb6S+x5C8kd4EavLr7AfdLN15FRGPyQrd7f+bpFJN1RhdpS3MdVkV3VmSWX2DP3uXuduLj3R9w3279lvugGx8HdMvkLfTvN4YdmY+OMZjGZ60DdXvVYJLZ1OLDPVeu9JBVeXFPd3cPkkIffh0yMWf6HibhfXyORHCG3S7M72GhdRjwSKOHMvz833/jfuvGcb7bpiVGF7ho2K/4aR1qJ5ybLOBhAq3XkP5P5DwJaACWo/umP9A" +
"A2INef3OvaUoXaRtq9kFRBGO0tFIUTJIDIlXyQtVCUga51JbkCWKJnHgj/SWfyRtRHPFTjZQk3aGeTi5SDUTqsg4UB8hlMhunKLfhTg72TjYymW1g8YFMMpJGg7yb5FL3pBngUWmIbERBwYn497iAAGW0hGOHSV1cgnTx+GG8fwjXiAxRM8FtVkIS5SPP/APzs5DjUOzMa7OmWeSZA6FbTegO4uvfPNk0tQnV6yhpNNOh5GlIxAumKQ4h2wV4hig7DkUBtnK5jWO4s+0IewcnqVwuDyjcnx2hQ97qVFG6pw/ShY2/2cjqAuNg4JjgYBeDmBs0aKirS1BQIkgSt" +
"EGoMTK6At8rS0wUN3EJCeX449JoA6J0RUEpex1t59FFQ+kJFiRHSdQr6ORuAn0acr8mySXKUBgapg/3jw71jPTxVI51DQ8v1YaO1gZF6LwjvAOhdNkYkpTJ5I6KUSMh0Dt8eUJwOMhHyUaOlHOsdziPMzqcCyHcYCF+EVOT0fily5iR9s8LOJdL6z3OfcnXGJPNIWw9d53hGCdiWVIxayOj0mJt9P02zngq5fDNLYBbBxYtB1heuz/jRtXm6Orq6M1VN95GaxX4Gr6Jp54eunQZDIQhy5cNPn3ataU528FUN3J8c4vLaSoHmn9eJrrT8xVPNa8aakAqNbErOwV" +
"Z09gp6A0FeUEwvE25SXS0Jb/cypibgmD2zL2XSoovDD1yBkES8ZPPVQgZDLrYEN+wwKAVY7JyolMWLssYXzg205UbNlA6EE1v3P3Se1UVCNUsOP2p6O7+5sbi8XPniAcMlw5Vd4QRqQ2XIc8uDZqU621wCHem8gsDCXuC6yBSISs+mV5N9EYXvoRevUJCyVRINILeJDJbgWId8uDCilIAUorKiye+2jKteGZxOsumTJtZXLAlJSbeq3hsWmp9Q0rKAPwH/gVqSvM7dhSWVMxIFaGUolklRdt3Tp0xa8Y4sLd3nQGXm8ZlZWWsBReJmLftSHMv509s296y/tNpl" +
"DqtsNbRypUSOb8gvIlvgbcD/ukKfuc2uIHqNn7nKv7Rgeb/IkhqWqdvQq+aFqFmU3GTfl0T7iR8PvYna57De1VbmxT5mG6yHfSVyiOErDVjRKeYWGYiaael9ktZDtWoA4AoSU+1KWiUGreKrr3FQpmcxwg7Ca9YOTV20hxIQ02IYPS0HWkppu2mHZP4emcm+viCh1dD6i59OMt6aeqfF0HB5LXPT52GjqnVaSlKd//gOXszM0GpaZiicuVKZ25rLpoBk+e5u6MBgxUylVo709N+wCDkMcLbfWRGQDDy8UxP8XD3cNDnZrc1Zo3Tjt+5sWAiypl0PN3fn1NrguK" +
"c1W5+OTkfLk7NiFEnjJ1wsLiI46YXoRHcWF1Fmov7SEcbqSjCwU0TX6l1sLen9dcZ5h/EIiI3FyaAiaE1USSyLqV4Aaj7CYAIyoP4kwensJMhiVjkrlHLbMj3RAgyJHg9F/BCHP744rz51bMvbywpsX1huMGQt2psSnrKiomR4X3xnPE8BEPwpY9Eoo8u4o/wRx8YW2HnK7//8ep2hLa/+sfvr+xEOfgQB80rrtxevoIdn3UoJyQE5Rc0ry4shTDt38Fw4wbL3riO38PvXrolEt26DFH44a4DAAd2gQSGdOxBaE+HkKOo2ShUKXqPrBN9afXy8S5IJOiEZIyuE" +
"/WhWneFVKNTqSW8lqkLgFGkrn3BEB0WXPwP5K0JSXB1HlHmE66bAkpJ2465MHSQjdq3wGUkmxgxJkKflBcR4RE+dNCAIQ6T9bYjJNdx7sdqSBw+dKhkmK1coCWCaWETWCMjolFTBnKJQs7yrxHQcu2aC31BWnjVeOnyVXhVeOf9JYO5wxlYV1o39KD+ImV39h1hv78D+3aaOsnzRLiI2lCbdR81EU2Ai8K+A/dIt4OIt3nStQ7I6TJYrxYKSu58vJCRJEAKT1ErOj598ugYjtPrMvD3EA2uKHL0xB/3/pxlOvoMGuXkv3F2iqy0UXYmFTudxb3LIsMH4VegELf" +
"Ddoi49aRCr4DhPyoUGE/uLioRTbPy6okM3N3GRkG+HmwNmkp4HU73cWQWRFZY3FRvQTa9JTZ5QHb2ovrMcQiNy6xflJ09PTwoY5wuGKHg0IyMoHCudUlONkLZ45c05EwAmJBTnxkSHq4blxmmRUgbKuiT4JiGG0i0OZyv0+mUlt1wuh8F/yRR6+A2OIDzTH9jvwM78G4HL3y7Hd/CXwr7U+4oCRURekfx+11CkU8sUYVYCKepCUls3KHc31mpmz1ob1ZK/fKsDDRE6o6vQA1K2girvcMCnZzQ0qWpYxFKSKvw9fDR4Lc28mNHoXoydiAfnyy7oLy1q6yhyvYxj" +
"qnEGi3v27xd9MSO9vZwcQ/21bgP3zckNaVu07g02UhDlLM9ysp68eegfc7lER5uCGm8otjzjW6jdAHOTiNHhdckJCCUnDo9xD/Awy0kOGxxSjL7RyHr5qyP8FAyT9Z0HMgi0ekkDMJfWUs69GwCzmXnkjhvZ8mBLaHoURaskaZeDA/PywsLu3CBK9kZPwbnit4wbS43REYaylFl749zFjvhFCF/MuDl7BHRUXo+AqRU5TYiumtAzPze6bkLiVF1wd5tbXg558VWzdwD6+CPq+KDD3P5vk5wHjzRJ1Srskf7pcK+spPGIzxcrdF4RES4a1B2pIb8RIZrvLw0VOb" +
"YFz4z99GTBwqtxLIhLUR+PZUu1YBaovl2xkTO3iU7wA8lJpYWlqvGJlSJHB18PNX+XP0AP7/R0/2DUECuE8rzGiFD9hqBnziUBLNEx/mamnD24NyqVXuJAZ3NPzK5bzNvjyqCdbOJfC1rUBrB+OxQUDVPP/EDscUP+ICn4QOWathgTc7GDbkewwcPHq7OffGl8eohw9pEKlVysrutmAMXj8QUdzmScA2TQ4I8J032CgiZlB8Y5JWf7xESNLnQMyjI0dE+rNArAKEAe0cHSos/wbpwK9Z56PQaugFLXgF2X7gALX2/0teWq5cvGXExLhbeaSzKNd8RpYqu01NQt" +
"PpNz3h40PMHWpLACecPQGD/GDtpdd9e2Ae6ffja3b1xcXv7Fq5atc9T1Nsj6tiIck2Hvvhi07JlcNkqIWHsPH5sZ8bj0dj/ze8s8yxnJz3XtxPJocLP2SWscuC+7JTFy7PT0ZARauKF83pXWidVbYSVPmGBzo5oaUN6EkKJqRU+xDDw2xtR+mMaiggNRy38jRFqrB6aJ/n7X90VMtmCVUQUswFC9mPj3X2jR+/rq4od7aUmPuyn8RhGfDi1blNGuu3IyGjHkSgre8MD4sMVxCtZVqOJtoosz3SQFxk7rtHNIcTfxWmko17w6bTioKBAd5U2KLwulfh0EbL6NGL" +
"icQprID7ryu+mEiYImcNBLLckVXqtNU3ShOhlofwG+vSLA4YoT4ZAuM6QodOii2hO5Stb5i1Acac8yAe/hY36wIBA89jY8YFBSBtS57W3qhqhill/XZ4HoIPJNLKxwhlB8S/MMCJHO7qOJSnkYJo+0uXsQEoIlSj7+1z80+Vb1z/Cv1qODPZVdD/XDZBwjp4cPCccGxQr7pEfIV7y40qW8WcPFYzqifOHA8F6eIOea+t/buNP5xL73mrIy2ugV3ZqaGgqvf58VFF8RGjQkCc8TyVzF5uxWM7vQbgwGlrXBleFnXSERKx01ailI0hK4qoPlY7QqJWuErF0BAnVW" +
"lBplBIVpS8SvO/cuXkdX8Y3bt2+dRP8QfLmqVNv4oe45xT56dwOM/Ym49qkfXBsE5CV5a3b+AZtevM2+JGut3sOn+okyx3a6RTuwQ9PneLi+/acPIkO47XEi/vTFsxEMolPp45VWoBTYgVQpV4bTA3b57GN+8DTKI+DtgUGg6NLcmZwcEzsDly6/cF6L9/o77rGuLs7qVK7nokVv+jojHFqT9cIw4TpSWPXzg93dVw6amRk8Eg7oun+vI15Ol8gtqKp9TSNkCJbjtLw4Po0ptBqp3EZ0dFJpxfMn1fz9ovTpnl1OIZpM5YkJiaMWZEVEe74LFwBXh0TI5fX13e" +
"9V7949OhlBleX/PyGhvx8H69Masv83q74Z35vV8uvVP/H/V3qRsSv5M++zwvV3aZT9/6n3V5U0E1+aIwLJxncbfZzGjH0A4H8KQi7AyEcSvArc6AICqvwizC3CrfjHa0wC+bU4JegqgZvxC01UI038GdJ+D1DwrOUkdP48e/7hlaentg/RKndptPd/2EXUTSUJw6YNWYze8FyZkQnWDERkIRWiC0HhPiDhbxl66QhKjRsyRzX8IaaSazx9zPH/tHSsmPFiuSmGlbeVZfp4s+yo2N6t3AVhw6c2X4gxrAPr198uGs2VNbxGKQ3P2AXi54jelOSOOUl7G/xZ39Ct" +
"Ur+zJZGKE+zOpWbsEXDWrhCB0Ux77x29uB+UN7cHLR79k9nf7za80Z+xcqy6TyHU8reWFvfWblmXMPWLWdNBqhMW6LLiGDPCCoQaoPc55Z9UIKpBPf4U8309DX7x1wcCWuucdfdS+byQGdDgLSL4lqX5fw1J9TBedp9hFPC/0st3MrFM9bEt1CWnqkwzo4SGGSZDPNVLooLIXcyiu786bE/5XjSfp9p5MpgfzCt8vQIi1Cr1R6R4e6elnt1RJiHJxxrb4eHEZ4ajWcEzQM9+76iSaHaEKHWkAxZTecU9tqeIzMqaG74n+q7Vqt8ss57lzrTv1V7Oc5ik3/SlZb" +
"XlfKRrr65RVR1Gf/8X3TFMjUMiEeJ9vO45/kM0YaISgMkRWETQX3n1vUb+Dq+S7DtOviCqLOz803ch01vvvlm50DkjAvmwcQvG0T7b9zCt2mrG7fAGzS3b/aQhiACrpM0xCbc92Yn56HpuxiFdiXz/tufpvhnoMmyWH+cLhEqlZIn9Kl5Gr0xvh7u3gkBfgEBqQGuTsGepVCBf9H4B82NTxifszZdFxb6LHzA/nBbu8CQxCRtkHSwGj5qODYkLWlBY+4EF+VoIYfPMT8UDxUd4c+F8rsPnEQpUdKgQd7sOIq/cuoejEal19LgQt5CGWUwJx4alfH79nO4O5DdH" +
"dO3hOPYsTEmuzDw+mj9T+PG9a7vw0vCv4tpIUvomNfCYQ1wLeDKXV9eUPvlgdWhw6X+q0/dmTvtWEwxOD1c7aD2dFj9B4xayPybvLOfQd79ZC1X9XeUx0dvtY9KKHzLp0n/ZGpIkMpNr3d2ahidUDszdrSLa9hzKanzFsWNaZBIZJ7BBg+NLjTzWVSAzrg4jQ30UCnddHPi42JHl8T7BbgaNs6LGxMZWe1sYytVqtwM6W5Kui/A7UE9ona+UqB6tFASzgjotY/r2ITPI4tGGMKneCp9HNzcnJxdHEYOXjRMqyvRqDQe6QZbV1duz+y4mEFDh44YPAzZ2oQWxif" +
"DsEHD3NEIO3o+A4ycB2qx1ivGsJ+DsatLsAd/MEp+efzMX/L+42el3FgIfPw/A6UXuLHXWvn/R3nymVQrp8+g3HJuvtQ8HQKZi9az9qUXtkMZrY0ncWNRpnUPmKzEpEpyQeMF0x+WkfnREZNF2k3q9z8xcjpK1oV5F/k5tlrmoeeLp6NJ/DyW87zWdnS+rXTOJ23L4xlsi5i79KnmcnF/xq1nwhX2QEPf8X+zccMz5HcEiWlR9z/Y+XB4GnkITP8C/0B7hZvKkOTu0hA/ZuWi+NS2554JQXLqXaEsNCgkKNLT0zcwuTktLT5u7pbj7bwdlbESLo6vSfypsjUGy" +
"qe3tJbMQGjG9K1bisvRzbaZ5QiVl217eWYFy1aU0pyivwy8eQR6KtrrwHLkX8pqn8b2Q9wK+6LnzGl4991n4nSDH6o1vVd2Ao3BnzB/pi+SSX0GLbHR8GTc1gkbZKF6Gdsfpejp9qdQDx2fD7UfbKeojIvLyl5SHz9WxA0bJLOz+VdIpMrdTRU12sXd3f2ZYhmL32Y5lk2Ir6pPThmXvlTl5uI8SqWxh/EmqbfH6Ghvb2+vmBhPL96HJqDpEhf+LJUdrVKpdPRMltJy5FMlpbuqUkj9Da39ndvB4YeluJfb9vM/v/oZTUenN240JaDXTNn0YtPpsde+vzL/Dyy" +
"zUD4AAHicrY0xigJBEEVf6+iyIEaLGHZoIIMDCnsANzDQQNDMQGTUhkFhZjzF5h7AE3kLr+EfKQMTI7uh+/H4VR9occZRHUeH2LjGFwvjuvy/cSS+GjdoOWfc5Mf9Kumib5n+Y6riGm3+jOvyK+NIfDFu0OVm3KTn2oyZMMOTMGSk1zMlUOqmZBRs2JPLbGUYT2Y+GY4SPw1lmWbFZp+Hrfxc4R0nDawVZp7uTtla8JHlH1myVDhXOHDk8FgWM9DPMs2LcDz4JB74Z9ez6X1PVfPacgcnT02VeJx9z81PzwEAx/HXt4ffLz0oRSGUVJ7zqzxUHqqfVJI85PkhZ" +
"FZtzVya2VxSInSy1akOmI2T5akLhx6szT8gh04dnMyBzmx19rm89jm+xfj/nhHEBLFixYkXEpZgiURJkqVYKlWaZdJlWG6FTFlWWmW1bGustU6OXOvl2SBfgUIbbbLZFltts12RHSKKlSi10y677VGmXIW99tnvgEpVqkUdVOOQWnXqHdbgiEZHNTnmuBNOanbKaWecdc55F1x0yWUtrrjqmud63ffJoB/6PDbslRdBnEe+6/E0iA9CBgx5aMJsEDbitT9+m//X3+q6r26Y1uaXdv063XbXmG4/vTHnpW9mjHrrg48mvfPelHvGPfDFZ09CbZ13brUXL1AS7rr" +
"ZEYlU1ywYXfzR0kXL/wIjEkhHAAAAAAAAAf//AAJ4nGNgZGBg4AFiMSBmYmAEwoVAzALmMQAACokA0AAAAHicY2BgYGQAgisSYoIMaAAAFToBFQAAAA==) 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];
}
}
}

24
Lora-Map/Model/UTMData.cs Normal file
View File

@ -0,0 +1,24 @@
using System;
using System.Text.RegularExpressions;
using CoordinateSharp;
namespace Fraunhofer.Fit.IoT.LoraMap.Model {
public struct UTMData {
public String MGRS;
public String Base;
public String FieldWidth;
public String FieldHeight;
public String Width;
public String Height;
public UTMData(Double latitude, Double longitude) {
this.MGRS = new Coordinate(latitude, longitude).MGRS.ToString();
String[] d = Regex.Split(this.MGRS, "([0-9]+[A-Z] [A-Z]+) ([0-9]{3})([0-9]{2}) ([0-9]{3})([0-9]{2})");
this.Base = d[1];
this.FieldWidth = d[2];
this.Width = d[3];
this.FieldHeight = d[4];
this.Height = d[5];
}
}
}

View File

@ -5,24 +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");
ADataBackend b = (ADataBackend)ABackend.GetInstance(ini.GetSection("mqtt"), ABackend.BackendType.Data);
new Server(b, ini.GetSection("webserver"), InIReader.GetInstance("requests"));
while(true) {
System.Threading.Thread.Sleep(1000);
}
Dictionary<String, String> backenddata = ini.GetSection("mqtt");
backenddata.Add("topic", "lora/#;camera/#;sfn/#");
ADataBackend b = (ADataBackend)ABackend.GetInstance(backenddata, ABackend.BackendType.Data);
_ = new Server(b, ini.GetSection("webserver"));
}
}
}

View File

@ -1,44 +0,0 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
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 - 08.03.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.1.3")]
[assembly: AssemblyFileVersion("1.1.3")]
/*
* 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
*/

View File

@ -1,67 +1,86 @@
using System;
using System.Collections.Generic;
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, Botclient> locations = new SortedDictionary<String, Botclient>();
public Server(ADataBackend backend, Dictionary<String, String> settings, InIReader requests) : base(backend, settings, requests) { }
class Server : Webserver {
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;
protected override void Backend_MessageIncomming(Object sender, BackendEvent e) {
public Server(ADataBackend backend, Dictionary<String, String> settings) : base(backend, settings, null) {
this.logger.SetPath(settings["loggingpath"]);
this.admin = new AdminModel(settings);
this.admin.SettingsUpdate += Settings.Instance.ReloadSettings;
this.admin.GeoUpdate += Settings.Instance.ReloadGeo;
this.admin.NamesUpdate += PositionModel.Instance.ReloadNames;
this.StartListen();
this.WaitForShutdown();
this.Dispose();
}
protected override void Backend_MessageIncomming(Object sender, BackendEvent mqtt) {
try {
JsonData d = JsonMapper.ToObject(e.Message);
if (d.ContainsKey("Rssi") && d["Rssi"].IsDouble
&& d.ContainsKey("Snr") && d["Snr"].IsDouble
&& d.ContainsKey("Receivedtime") && d["Receivedtime"].IsString
&& d.ContainsKey("BatteryLevel") && d["BatteryLevel"].IsDouble
&& d.ContainsKey("Gps") && d["Gps"].IsObject
&& d["Gps"].ContainsKey("Latitude") && d["Gps"]["Latitude"].IsDouble
&& d["Gps"].ContainsKey("Longitude") && d["Gps"]["Longitude"].IsDouble
&& d["Gps"].ContainsKey("LastLatitude") && d["Gps"]["LastLatitude"].IsDouble
&& d["Gps"].ContainsKey("LastLongitude") && d["Gps"]["LastLongitude"].IsDouble
&& d["Gps"].ContainsKey("Hdop") && d["Gps"]["Hdop"].IsDouble
&& d["Gps"].ContainsKey("Fix") && d["Gps"]["Fix"].IsBoolean
&& d["Gps"].ContainsKey("Height") && d["Gps"]["Height"].IsDouble
&& d.ContainsKey("Name") && d["Name"].IsString) {
String name = (String)d["Name"];
Botclient b = new Botclient(d);
if (this.locations.ContainsKey(name)) {
this.locations[name] = b;
} else {
this.locations.Add(name, b);
}
Console.WriteLine("Koordinate erhalten!");
}
} catch (Exception ex) {
Helper.WriteError(ex.Message);
JsonData d = JsonMapper.ToObject(mqtt.Message);
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);
}
}
protected override void SendResponse(HttpListenerContext cont) {
if (cont.Request.Url.PathAndQuery.StartsWith("/loc")) {
try {
Dictionary<String, Object> ret = new Dictionary<String, Object>();
Byte[] buf = Encoding.UTF8.GetBytes(JsonMapper.ToJson(this.locations));
cont.Response.ContentLength64 = buf.Length;
cont.Response.OutputStream.Write(buf, 0, buf.Length);
Console.WriteLine("200 - " + cont.Request.Url.PathAndQuery);
return;
} catch (Exception e) {
Helper.WriteError("500 - " + e.Message);
cont.Response.StatusCode = 500;
return;
}
protected override Boolean SendWebserverResponse(HttpListenerContext cont) {
try {
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);
}
} else if(cont.Request.Url.AbsolutePath.StartsWith("/api/time")) {
return SendJsonResponse(new Dictionary<String, DateTime>() { { "utc", DateTime.UtcNow } }, cont);
} else if(cont.Request.Url.AbsolutePath.StartsWith("/api/svg/")) {
return SvgModel.Instance.ParseRequest(cont);
} else if(cont.Request.Url.PathAndQuery.StartsWith("/admin/")) {
return this.admin.ParseReuqest(cont);
} else if(cont.Request.Url.PathAndQuery.StartsWith("/maps/")) {
return SendFileResponse(cont, "resources", false);
}
} catch(Exception e) {
Helper.WriteError("SendWebserverResponse(): 500 - " + e.Message + "\n\n" + e.StackTrace);
cont.Response.StatusCode = 500;
return false;
}
base.SendResponse(cont);
return SendFileResponse(cont);
}
public override void Dispose() {
SensorModel.Instance.Dispose();
base.Dispose();
}
}
}

View File

@ -1,3 +0,0 @@
[js/nav.js]
your_api_key=abc
start_location=50.7, 7.2

View File

@ -3,4 +3,7 @@ type=mqtt
server=127.0.0.1
[webserver]
prefix=http://+:8080/
prefix=http://+:8080/
admin_user=admin
admin_pass=password
loggingpath=/var/log/loramap.log

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)
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

@ -1,2 +1,4 @@
mkdir ..\..\..\Builds
bash.exe -c "./make-deb.sh armhf"
bash.exe -c "./make-deb.sh amd64"
pause

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

@ -0,0 +1,122 @@
html, body {
height: 100%;
margin: 0;
padding: 0;
font-family: Verdana;
}
#header {
font-size: 20px;
font-weight: bold;
padding: 5px;
}
#menu {
width: 200px;
font-size: 12px;
float: left;
}
#menu ul {
padding-left: 20px;
}
#menu ul li a {
color: black;
}
#menu ul li a:hover {
text-decoration: none;
}
#content {
margin-left: 210px;
border-left: 1px solid black;
}
#content .settingstable {
margin-left: 15px;
border-collapse: separate;
}
#content .settingstable thead {
background-color: #CCCCCC;
background-color: rgba(0,0,0,0.2);
}
#content .settingstable thead th {
text-align: left;
}
#content .settingstable tbody tr:nth-child(odd) {
background-color: #f39d9d;
background-color: rgba(20,0,250,0.1);
}
#content .settingstable tbody tr:nth-child(even) {
background-color: #9c9eee;
background-color: rgba(250,59,0,0.1);
}
#content .settingstable tbody tr:hover {
background-color: #e4e1e1;
background-color: rgba(0,0,0,0.1);
}
#content .settingstable tfoot {
background-color: #e4e1e1;
background-color: rgba(0,0,0,0.1);
}
#content .pointer {
cursor: pointer;
}
#content #nameeditor .title {
margin-bottom: 20px;
font-weight: bold;
}
#iconeditor {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #333232;
background-color: rgba(0,0,0,0.8);
}
#iconeditor .innerbox {
margin: 100px;
background-color: white;
border: 5px solid black;
border-radius: 20px;
padding: 15px;
}
#iconeditor .innerbox .preview {
float: left;
}
#iconeditor .innerbox .controls {
margin-left: 220px;
}
#iconeditor .innerbox .save {
clear: both;
}
#content #eximport .title {
margin-bottom: 20px;
font-weight: bold;
}
#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

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Adminpannel Lora-Map</title>
<link rel="stylesheet" type="text/css" href="css/global.css" />
</head>
<body>
<div id="header">Adminpannel Lora-Map</div>
<div id="menu">
<ul>
<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

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

View File

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

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="de-de" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Lora-Map Admin</title>
<style type="text/css">
body {
font-family: Verdana;
font-size: 13px;
}
.label {
width: 100px;
display: inline-block;
font-weight: bold;
}
.login {
margin-left: 105px;
}
div {
margin-bottom: 5px;
}
</style>
</head>
<body>
<h1>Login to Lora-Map Admin!</h1>
<form method="post" action="/admin/login">
<div class="user">
<span class="label">Username: </span>
<input name="user"/>
</div>
<div class="pass">
<span class="label">Password: </span>
<input name="pass" type="password"/>
</div>
<div class="login">
<input type="submit" />
</div>
</form>
</body>
</html>

View File

@ -3,6 +3,46 @@
#bigmap {
height: 100%;
}
#bigmap .marker-alert {
border: red 5px dashed;
top: -5px;
left: -5px;
border-radius: 5px;
box-shadow: rgba(0, 0, 0, 0.4) 10px 10px;
}
#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;
text-shadow: 3px 3px 3px #000000;
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 {
@ -11,52 +51,72 @@ html, body {
padding: 0;
}
object {
pointer-events: none;
}
.leaflet-touch .leaflet-bar a {
width: 38px;
}
#menucollumn {
width: 30px;
width: 32px;
background-color: white;
background-clip: padding-box;
position: absolute;
top: 85px;
bottom: 35px;
bottom: 10px;
left: 10px;
z-index: 5000;
border: #707070 2px solid;
border: rgba(0,0,0,0.4) 2px solid;
border-radius: 4px
border: rgba(0,0,0,0.2) 2px solid;
border-radius: 4px;
padding: 3px;
}
#menucollumn span {
display: block;
height: 32px;
width: 32px;
margin-bottom: 5px;
}
#menucollumn .pos {
display: block;
height: 30px;
width: 30px;
background-image: url("icons/marker.png");
background-image: url("icons/placeholder.png");
}
#version {
position: absolute;
bottom: 0;
left: 0;
z-index: 50000;
background-color: white;
border-radius: 5px;
height: 20px;
width: 40px;
border: #707070 2px solid;
border: rgba(0,0,0,0.4) 2px solid;
#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;
top: 85px;
left: 45px;
bottom: 35px;
left: 55px;
bottom: 10px;
width: 250px;
z-index: 50000;
background-color: white;
background-clip: padding-box;
border: #707070 2px solid;
border: rgba(0,0,0,0.4) 2px solid;
border: rgba(0,0,0,0.2) 2px solid;
border-radius: 4px;
display: none;
font-size: 11px;
font-family: Verdana;
overflow: auto;
}
#pannels #pannels_pos {
display: none;
@ -64,6 +124,10 @@ html, body {
#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;
@ -76,10 +140,10 @@ html, body {
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;
}
@ -109,11 +173,231 @@ html, body {
}
#pannels #pannels_info .coord {
font-family: monospace;
font-size: 14px;
font-size: 22px;
font-weight: bold;
}
#pannels #pannels_info .hdop {
#pannels #pannels_info .lastgps {
margin-bottom: 10px;
}
#pannels #pannels_info .update {
margin-bottom: 10px;
}
#pannels #pannels_info .alerts {
margin-top: 10px;
}
#pannels #pannels_info .alerts .panicitem {
display: block;
}
#pannels #pannels_version {
padding: 5px;
display: none;
}
#pannels #pannels_version .versionstring {
font-weight: bold;
}
#pannels #pannels_version .versionstring #version {
font-weight: normal;
font-family: monospace;
display: inline;
}
#pannels #pannels_version .cicons {
font-weight: bold;
}
#pannels #pannels_version .cicons ul {
margin: 0;
padding: 0;
padding-left: 15px;
}
#pannels #pannels_version .cicons ul li {
font-weight: normal;
}
#pannels #pannels_admin {
padding: 5px;
display: none;
}
#pannels #pannels_admin a {
color: black;
text-decoration: underline;
font-size: 18px;
font-weight: bold;
}
#pannels #pannels_admin a:hover {
text-decoration: none;
}
#pannels #pannels_admin div {
margin-bottom: 5px;
}
#pannels #pannels_admin div .label {
width: 70px;
font-weight: bold;
display: inline-block;
}
#pannels #pannels_admin div input {
width: 150px;
}
#pannels #pannels_admin div .login input {
width: auto;
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;
left: 61px;
z-index: 50000;
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;
text-align: center;
}
#overlays #cameracount .camera .name {
font-weight: bold;
}
#overlays #cameracount .camera .in::after {
content: url("../icons/general/bullet_add.png");
}
#overlays #cameracount .camera .out::after {
content: url("../icons/general/bullet_delete.png");
}
#overlays #cameracount .camera .total::after {
content: url("../icons/general/bullet_star.png");
}
#overlays #crwoddensy {
position: absolute;
top: 10px;
right: 90px;
z-index: 50000;
font-size: 11px;
font-family: "Verdana";
padding: 3px;
max-width: 500px;
}
#overlays #crwoddensy .camera {
background-color: white;
border: rgba(0,0,0,0.3) solid 2px;
border-radius: 5px;
padding: 4px;
float: right;
max-width: 100px;
margin-left: 5px;
}
#overlays #crwoddensy .camera span {
display: block;
text-align: center;
}
#overlays #crwoddensy .camera .name {
font-weight: bold;
}
#overlays #crwoddensy .camera .count::after {
content: url("../icons/general/bullet_star.png");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="74.953316mm"
height="84.703323mm"
viewBox="-37.5 -12 74.953316 84.703323">
<g id="person">
<defs id="people-def">
<style type="text/css">
#person-layer-org g {
display: none;
}
#person-layer-funct g {
display: none;
}
#person-layer-rang g {
display: none;
}
#person-layer-typ g {
display: none;
}
#person-layer-org rect {
stroke: black;
stroke-width: 3px;
}
#person-layer-funct path {
stroke: #000000;
stroke-width: 3px;
fill: #000000;
}
#person-layer-rang circle {
fill: #000000;
}
#person-layer-typ tspan {
font-size: 20px;
font-family: DIN1451;
text-align: center;
text-anchor: middle;
fill: #000000;
}
#person-layer-typ line {
stroke-width: 3px;
stroke: black;
}
</style>
<style type="text/css">
#person-layer-org #person-layer-org-thw {display:inline;}
#person-layer-typ #person-layer-typ-loesch {display:inline;}
</style>
</defs>
<g inkscape:groupmode="layer" id="person-layer-org" inkscape:label="Organisation">
<g inkscape:groupmode="layer" id="person-layer-org-thw" inkscape:label="THW">
<rect style="fill: #0069b4;" height="50" width="50" transform="rotate(45)" />
</g>
<g inkscape:groupmode="layer" id="person-layer-org-fw" inkscape:label="Feuerwehr">
<rect style="fill: #e30613;" height="50" width="50" transform="rotate(45)" />
</g>
<g inkscape:groupmode="layer" id="person-layer-org-hilo" inkscape:label="Hilo">
<rect style="fill: #ffffff;" height="50" width="50" transform="rotate(45)" />
</g>
<g inkscape:groupmode="layer" id="person-layer-org-pol" inkscape:label="Polizei">
<rect style="fill: #13a538;" height="50" width="50" transform="rotate(45)" />
</g>
<g inkscape:groupmode="layer" id="person-layer-org-fueh" inkscape:label="Führung">
<rect style="fill: #ffed00;" height="50" width="50" transform="rotate(45)" />
</g>
<g inkscape:groupmode="layer" id="person-layer-org-sonst" inkscape:label="Sonstig">
<rect style="fill: #ec6725;" height="50" width="50" transform="rotate(45)" />
</g>
</g>
<g inkscape:groupmode="layer" id="person-layer-funct" inkscape:label="Funktion">
<g inkscape:groupmode="layer" id="person-layer-funct-sonder" inkscape:label="Sonder">
<path d="M -10,10 H 10" />
</g>
<g inkscape:groupmode="layer" id="person-layer-funct-fueh" inkscape:label="Führungskraft">
<path d="M -10,10 H 10 L 0,0 Z" />
</g>
</g>
<g inkscape:groupmode="layer" id="person-layer-rang" inkscape:label="Rang">
<g inkscape:groupmode="layer" id="person-layer-rang-trupp" inkscape:label="Truppführer">
<circle cx="0" cy="-8" r="3" />
</g>
<g inkscape:groupmode="layer" id="person-layer-rang-grupp" inkscape:label="Gruppenführer">
<g style="display: inline;">
<circle cx="-4.5" cy="-8" r="3" />
<circle cx="4.5" cy="-8" r="3" />
</g>
</g>
<g inkscape:groupmode="layer" id="person-layer-rang-zug" inkscape:label="Zugführer">
<g style="display: inline;">
<circle cx="-9" cy="-8" r="3" />
<circle cx="0" cy="-8" r="3" />
<circle cx="9" cy="-8" r="3" />
</g>
</g>
</g>
<g inkscape:groupmode="layer" id="person-layer-typ" inkscape:label="Typ">
<text><tspan y="42" x="0" id="person-layer-typ-text"></tspan></text>
<g id="person-layer-typ-loesch">
<line x1="-35" y1="35.5" x2="35" y2="35.5" />
<line x1="17.5" y1="35.5" x2="25" y2="26" />
<line x1="17.5" y1="35.5" x2="25" y2="44" />
</g>
<g id="person-layer-typ-sani">
<line x1="0" y1="0" x2="0" y2="70" />
<line x1="-35" y1="35.5" x2="35" y2="35.5" />
</g>
<g id="person-layer-typ-betreu">
<line x1="-17" y1="53" x2="0" y2="35.5" />
<line x1="17" y1="53" x2="0" y2="35.5" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,406 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="75mm"
height="53mm"
viewBox="0 0 75 53"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="Trupps-Gruppen-Zuege.svg"
inkscape:export-filename="E:\Eigene Dateien\Dokumente\Visual Studio 2017\Projects\mqtt-map\Lora-Map\resources\icons\marker\thw\trupp\B1.png"
inkscape:export-xdpi="50.799999"
inkscape:export-ydpi="50.799999">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="469.68393"
inkscape:cy="210.67759"
inkscape:document-units="mm"
inkscape:current-layer="layer15"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Background"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-244)">
<rect
style="opacity:1;fill:#0d468b;fill-opacity:1;stroke:#e6e6e6;stroke-width:5;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect10"
width="70"
height="40"
x="2.5"
y="254.5"
rx="0"
ry="0" />
<g
aria-label="THW"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.24502373px;line-height:125%;font-family:Consolas;-inkscape-font-specification:Consolas;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28112558px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4534">
<path
d="m 51.057801,293 v -6.92382 H 48.866998 V 285 h 5.595059 v 1.07618 H 52.271254 V 293 Z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28112558px"
id="path4536"
inkscape:connector-curvature="0" />
<path
d="m 59.705708,293 v -3.49211 H 56.532063 V 293 h -1.213452 v -8 h 1.213452 v 3.42622 h 3.173645 V 285 h 1.213452 v 8 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28112558px"
id="path4538"
inkscape:connector-curvature="0" />
<path
d="m 63.895139,293 -2.004118,-8 h 1.284831 l 1.257379,5.6884 h 0.02196 L 66.009071,285 h 0.873027 l 1.553878,5.6884 h 0.02196 L 69.715317,285 h 1.284832 l -2.004118,8 h -1.010295 l -1.531915,-5.76527 h -0.02196 L 64.905434,293 Z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28112558px"
id="path4540"
inkscape:connector-curvature="0" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Größe"
transform="translate(0,-244)"
style="display:inline">
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Trupp"
style="display:inline">
<circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path4547"
cy="247"
cx="39"
r="3" />
</g>
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="Gruppen"
style="display:none">
<g
id="g4557">
<circle
r="3"
cy="247"
cx="20.25"
id="path4550"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" />
<circle
r="3"
cy="247"
cx="57.75"
id="path4552"
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="Züge"
style="display:none">
<circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path4550-0"
cx="20.25"
cy="247"
r="3" />
<circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path4552-7"
cx="39"
cy="247"
r="3" />
<circle
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path4547-2"
cy="247"
cx="57.75"
r="3" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer7"
inkscape:label="Zusatz"
style="display:inline">
<g
inkscape:groupmode="layer"
id="layer11"
inkscape:label="EGS"
style="display:none">
<text
id="text4622"
y="49"
x="3.9890261"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.23731136px;line-height:125%;font-family:Consolas;-inkscape-font-specification:Consolas;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.28093278px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28093278px"
y="49"
x="3.9890261"
id="tspan4620"
sodipodi:role="line">EGS</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer9"
inkscape:label="B"
style="display:none">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.23731136px;line-height:125%;font-family:Consolas;-inkscape-font-specification:Consolas;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.28093278px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="3.9890261"
y="49"
id="text4616"><tspan
sodipodi:role="line"
id="tspan4614"
x="3.9890261"
y="49"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28093278px">B</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer10"
inkscape:label="ASH"
style="display:none">
<text
id="text4610"
y="49"
x="3.9890261"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.23731136px;line-height:125%;font-family:Consolas;-inkscape-font-specification:Consolas;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.28093278px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28093278px"
y="49"
x="3.9890261"
id="tspan4608"
sodipodi:role="line">ASH</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer8"
inkscape:label="A"
style="display:none">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.23731136px;line-height:125%;font-family:Consolas;-inkscape-font-specification:Consolas;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.28093278px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="3.9890261"
y="49"
id="text4592"><tspan
sodipodi:role="line"
id="tspan4590"
x="3.9890261"
y="49"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28093278px">A</tspan></text>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer13"
inkscape:label="Bezeichnungen">
<g
inkscape:groupmode="layer"
id="layer18"
inkscape:label="TZ-E"
style="display:none">
<text
id="text4701"
y="34.989704"
x="16.894501"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px"
y="34.989704"
x="16.894501"
id="tspan4699"
sodipodi:role="line">TZ-E</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer15"
inkscape:label="TZ-FGr"
style="display:none">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="7.0419998"
y="34.989704"
id="text4695"><tspan
sodipodi:role="line"
id="tspan4693"
x="7.0419998"
y="34.989704"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px">TZ-FGr</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer17"
inkscape:label="TZ-BrB"
style="display:none">
<text
id="text4689"
y="34.989704"
x="5.9194999"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px"
y="34.989704"
x="5.9194999"
id="tspan4687"
sodipodi:role="line">TZ-BrB</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer16"
inkscape:label="TZ-Bel"
style="display:none">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="8.2465"
y="34.989704"
id="text4683"><tspan
sodipodi:role="line"
id="tspan4681"
x="8.2465"
y="34.989704"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px">TZ-Bel</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer14"
inkscape:label="TZ"
style="display:none">
<text
id="text4668"
y="34.989704"
x="27.107"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px"
y="34.989704"
x="27.107"
id="tspan4666"
sodipodi:role="line">TZ</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer19"
inkscape:label="FGr"
style="display:none">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="22.85"
y="34.989704"
id="text4662"><tspan
sodipodi:role="line"
id="tspan4660"
x="22.85"
y="34.989704"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px">FGr</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer22"
inkscape:label="Bel"
style="display:none">
<text
id="text4656"
y="34.989704"
x="24.054501"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px"
y="34.989704"
x="24.054501"
id="tspan4654"
sodipodi:role="line">Bel</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer21"
inkscape:label="B2"
style="display:none">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="27.250999"
y="34.989704"
id="text4650"><tspan
sodipodi:role="line"
id="tspan4648"
x="27.250999"
y="34.989704"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px">B2</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer20"
inkscape:label="B1"
style="display:inline">
<text
id="text4644"
y="34.989704"
x="29.015499"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px"
y="34.989704"
x="29.015499"
id="tspan4642"
sodipodi:role="line">B1</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="layer23"
inkscape:label="B"
style="display:none">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.29522419px;line-height:125%;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28238061px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="32.429501"
y="34.989704"
id="text4640"><tspan
sodipodi:role="line"
id="tspan4638"
x="32.429501"
y="34.989704"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.08441925px;font-family:'DIN 1451 Mittelschrift';-inkscape-font-specification:'DIN 1451 Mittelschrift';fill:#ffffff;fill-opacity:1;stroke-width:0.28238061px">B</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Some files were not shown because too many files have changed in this diff Show More