diff --git a/README.md b/README.md index 48b897a..9eefcbb 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Successfully built this on the Raspberry Pi (Raspbian Buster) and a desktop PC ( - Measuring tool for measure distances between points - Map stays focused on the same point across page refreshes - Clicking gateway or data point markers pops up information about them + - Toggle visibility of markers of each device on map + - Select age of markers to show on map ### TODO @@ -40,19 +42,20 @@ Make sure you have your application set up on The Things Network with the integr ### Install dependencies ``` -sudo apt-get install libffi-dev libssl-dev +sudo apt-get install -y libffi-dev libssl-dev ``` ### Install docker and docker-compose ``` curl -sSL https://get.docker.com | sh -sudo pip install docker-compose +sudo apt-get install -y python3 python3-pip +sudo pip3 install docker-compose ``` ### Add user to docker group -```sudo usermod -a -G docker YOUR_USER``` +```sudo usermod -a -G docker $USER``` Then log out and back in again. diff --git a/flask_app/Dockerfile b/flask_app/Dockerfile index b9820d6..b66018c 100644 --- a/flask_app/Dockerfile +++ b/flask_app/Dockerfile @@ -4,10 +4,13 @@ RUN mkdir -pv /home/project/flask_app RUN mkdir -pv /var/ttn_tracker/ssl_certs WORKDIR /home/project/flask_app -COPY requirements.txt /home/project/flask_app + RUN pip install --upgrade pip + +COPY requirements.txt /home/project/flask_app RUN pip install --no-cache-dir -r requirements.txt -COPY . /home/project/flask_app - +COPY ssl-certs-generate.sh /home/project/flask_app/ssl-certs-generate.sh RUN /bin/bash /home/project/flask_app/ssl-certs-generate.sh + +COPY . /home/project/flask_app diff --git a/flask_app/app.py b/flask_app/app.py index 0795523..20b57e9 100644 --- a/flask_app/app.py +++ b/flask_app/app.py @@ -9,6 +9,12 @@ from math import sqrt import os import requests from apscheduler.schedulers.background import BackgroundScheduler +from dateutil.parser import parser +from flask import Flask +from flask import jsonify +from flask import render_template +from flask_sqlalchemy import SQLAlchemy + from config import app_key from config import application from config import bing_api_key @@ -19,11 +25,6 @@ from config import path_db from config import refresh_period_seconds from config import start_lat from config import start_lon -from dateutil.parser import parser -from flask import Flask -from flask import jsonify -from flask import render_template -from flask_sqlalchemy import SQLAlchemy logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s') logger = logging.getLogger(__name__) @@ -90,6 +91,7 @@ def main_page(): get_new_data() return render_template('map.html', bing_api_key=bing_api_key, + devices=devices, gateway_locations=gateway_locations, location_data=Location.query.all(), refresh_period_seconds=refresh_period_seconds, diff --git a/flask_app/requirements.txt b/flask_app/requirements.txt index 60e9b0d..b3aee60 100644 --- a/flask_app/requirements.txt +++ b/flask_app/requirements.txt @@ -1,6 +1,6 @@ -apscheduler -python-dateutil -gunicorn -Flask==1.1.1 -flask_sqlalchemy -requests +apscheduler==3.6.3 +python-dateutil==2.8.1 +gunicorn==20.0.4 +Flask==1.1.2 +flask_sqlalchemy==2.4.4 +requests==2.25.1 diff --git a/flask_app/templates/map.html b/flask_app/templates/map.html index 26b1be7..0b9bf69 100644 --- a/flask_app/templates/map.html +++ b/flask_app/templates/map.html @@ -58,8 +58,7 @@ var default_zoom = true; b = JSON.parse(localStorage.getItem('bounds')); - if (b == null) - { + if (b == null) { map = L.map('mapid', {layers: [esri_Map]}).setView([{{start_lat}}, {{start_lon}}], 15); } else { @@ -78,7 +77,6 @@ //disable inertia because it is irritating and slow map.options.inertia=false; - var measureControl = L.control.measure({ activeColor: '#FF0000', completedColor: '#FF8000', @@ -112,6 +110,53 @@ var gateway = L.marker([{{each_gateway[1]}}, {{each_gateway[2]}}]).bindPopup('Gateway: {{each_gateway[0]}}<br />Lat/Lon: {{each_gateway[1]}}, {{each_gateway[2]}}').addTo(map); {% endfor %} + var markers = {}; + {% for each_device in devices %} + markers['{{each_device}}'] = new L.FeatureGroup(); + map.addLayer(markers['{{each_device}}']); + {% endfor %} + + var legend_age = L.control({position: 'topright'}); + legend_age.onAdd = function(map) { + var div = L.DomUtil.create('div', 'info legend_age'); + div.innerHTML = '<select>' + + '<option value="7day">Past 7 Days</option>' + + '<option value="1day">Past 1 Day</option>' + + '<option value="6hour">Past 6 Hours</option>' + + '<option value="1hour">Past 1 Hour</option>' + + '<option value="30min">Past 30 Min</option>' + + '</select>'; + div.firstChild.onmousedown = div.firstChild.ondblclick = L.DomEvent.stopPropagation; + return div; + }; + legend_age.addTo(map); + + function toggleFunction(element) { + if (element.checked) { + map.addLayer(markers[element.value]); + } else { + map.removeLayer(markers[element.value]); + } + } + + var legend_devices = L.control({position: 'topright'}); + legend_devices.onAdd = function(map) { + var div = L.DomUtil.create('div'); + div.innerHTML = ` + <div class="leaflet-control-layers leaflet-control-layers-expanded"> + <form> + {% for each_device in devices -%} + <div> + <input class="leaflet-control-layers-overlays" id="command" + onclick=toggleFunction(this) type="checkbox" value="{{each_device}}" checked>{{each_device}}</input> + </div> + {%- endfor %} + </form> + </div>`; + return div; + }; + legend_devices.addTo(map); + function add_new_markers(past_seconds) { var url = '/dsf673bh_past/' + past_seconds; $.getJSON(url, @@ -121,13 +166,36 @@ var node = L.circleMarker([parseFloat(data[i]["latitude"]), parseFloat(data[i]["longitude"])], { color: 'red', radius: 5 - }).bindPopup('Node: ' + data[i]["device_id"] + '<br />' + data[i]["datetime"] + '<br />Lat/Lon: ' + data[i]["latitude"] + ', ' + data[i]["longitude"] + '<br />Altitude: ' + data[i]["altitude"] + ' m, hdop: ' + data[i]["hdop"]).addTo(map); + }); + node.bindPopup('Node: ' + data[i]["device_id"] + '<br />' + data[i]["datetime"] + '<br />Lat/Lon: ' + data[i]["latitude"] + ', ' + data[i]["longitude"] + '<br />Altitude: ' + data[i]["altitude"] + ' m, hdop: ' + data[i]["hdop"]); + markers[data[i]["device_id"]].addLayer(node); } } } ); } + $('select').change(function(){ + for (var key in markers) { + map.removeLayer(markers[key]); + markers[key] = new L.FeatureGroup(); + map.addLayer(markers[key]); + } + if ($(this).val() === "all") { + add_new_markers(0); + } else if ($(this).val() === "7day") { + add_new_markers(604800); + } else if ($(this).val() === "1day") { + add_new_markers(86400); + } else if ($(this).val() === "6hour") { + add_new_markers(21600); + } else if ($(this).val() === "1hour") { + add_new_markers(3600); + } else if ($(this).val() === "30min") { + add_new_markers(1800); + } + }); + // Repeat function for getLastData() function repeat_add_new_markers(past_seconds) { setInterval(function () {