Commit bde56baa by Corey Koval

Merge branch 'webui'

parents a8bfae97 00bf94e5
......@@ -6,3 +6,4 @@ CesiumJS/
test*
*.db
*.txt
*.czml
......@@ -4,6 +4,8 @@
- [numpy](https://numpy.org/install/)
- [scikit-learn](https://scikit-learn.org/stable/install.html)
- [python-geojson](https://python-geojson.readthedocs.io/en/latest/)
- [czml3](https://pypi.org/project/czml3/)
- [geojson](https://pypi.org/project/geojson/)
## Usage: df-aggregator.py [options]
......
......@@ -5,12 +5,17 @@ import numpy as np
import math
import time
import sqlite3
import threading
import signal
from optparse import OptionParser
from os import system, name
from os import system, name, kill, getpid
from lxml import etree
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from geojson import Point, MultiPoint, Feature, FeatureCollection
from czml3 import Packet, Document, Preamble
from bottle import route, run, request, get, post, redirect, template, static_file
d = 40000 #meters
......@@ -65,7 +70,6 @@ class receiver:
confidence = 0
doa_time = 0
def plot_polar(lat_a, lon_a, lat_a2, lon_a2):
# Convert points in great circle 1, degrees to radians
p1_lat1_rad = math.radians(lat_a)
......@@ -133,14 +137,14 @@ def process_data(database_name, outfile, eps, min_samp):
c.execute("SELECT COUNT(*) FROM intersects")
n_intersects = int(c.fetchone()[0])
#print(n_intersects)
c.execute("SELECT latitude, longitude FROM intersects")
c.execute("SELECT latitude, longitude, num_parents FROM intersects")
intersect_array = np.array(c.fetchall())
# print(intersect_array)
likely_location = []
best_point = []
weighted_location = []
if intersect_array.size != 0:
if eps > 0:
X = StandardScaler().fit_transform(intersect_array)
X = StandardScaler().fit_transform(intersect_array[:,0:2])
# Compute DBSCAN
db = DBSCAN(eps=eps, min_samples=min_samp).fit(X)
......@@ -149,59 +153,154 @@ def process_data(database_name, outfile, eps, min_samp):
labels = db.labels_
intersect_array = np.column_stack((intersect_array, labels))
# print(intersect_array)
# Number of clusters in labels, ignoring noise if present.
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
n_noise_ = list(labels).count(-1)
clear()
clear(debugging)
print('Number of clusters: %d' % n_clusters_)
print('Outliers Removed: %d' % n_noise_)
# print(intersect_array)
for x in range(n_clusters_):
cluster = np.array([]).reshape(0,2)
cluster = np.array([]).reshape(0,3)
for y in range(len(intersect_array)):
if intersect_array[y][2] == x:
cluster = np.concatenate((cluster, [intersect_array[y][0:2]]), axis = 0)
likely_location.append(Reverse(np.mean(cluster, axis=0).tolist()))
best_point = Feature(properties = best_pt_style, geometry = MultiPoint(tuple(likely_location)))
if intersect_array[y][-1] == x:
cluster = np.concatenate((cluster, [intersect_array[y][0:-1]]), axis = 0)
weighted_location.append(np.average(cluster[:,0:2], weights=cluster[:,2], axis=0).tolist())
likely_location.append(np.mean(cluster[:,0:2], axis=0).tolist())
for x in likely_location:
print(Reverse(x))
print(x)
else:
likely_location = None
for x in intersect_array:
try:
if x[2] >= 0:
if x[-1] >= 0:
intersect_list.append(Reverse(x[0:2].tolist()))
except IndexError:
intersect_list.append(Reverse(x.tolist()))
#print(intersect_list)
all_the_points = Feature(properties = all_pt_style, geometry = MultiPoint(tuple(intersect_list)))
#all_the_points = Feature(properties = all_pt_style, geometry = MultiPoint(tuple(intersect_list)))
with open(outfile, "w") as file1:
if eps > 0:
return likely_location, intersect_list, weighted_location
else:
print("No Intersections.")
return None
def write_geojson(best_point, all_the_points):
if all_the_points != None:
all_the_points = Feature(properties = all_pt_style, geometry = MultiPoint(tuple(all_the_points)))
with open(geofile, "w") as file1:
if best_point != None:
best_point = Feature(properties = best_pt_style, geometry = MultiPoint(tuple(best_point)))
file1.write(str(FeatureCollection([best_point, all_the_points])))
else:
file1.write(str(FeatureCollection([all_the_points])))
print(f"Wrote file {geofile}")
else:
print("No Intersections.")
def write_czml(best_point, all_the_points, weighted_point):
print(best_point)
point_properties = {
"pixelSize":5.0,
"heightReference":"RELATIVE_TO_GROUND",
"color": {
"rgba": [255, 0, 0, 255],
}
}
best_point_properties = {
"pixelSize":20.0,
"heightReference":"RELATIVE_TO_GROUND",
"color": {
"rgba": [0, 255, 0, 255],
}
}
weighted_properties = {
"pixelSize":20.0,
"heightReference":"RELATIVE_TO_GROUND",
"color": {
"rgba": [0, 0, 255, 255],
}
}
top = Preamble(name="Geolocation Data")
all_point_packets = []
best_point_packets = []
weighted_point_packets = []
if all_the_points != None:
for x in all_the_points:
all_point_packets.append(Packet(id=str(x[1]) + ", " + str(x[0]),
point=point_properties,
position={"cartographicDegrees": [ x[0], x[1], 5 ]}))
if best_point != None:
for x in best_point:
best_point_packets.append(Packet(id=str(x[0]) + ", " + str(x[1]),
point=best_point_properties,
position={"cartographicDegrees": [ x[1], x[0], 15 ]}))
if weighted_point != None:
for x in weighted_point:
weighted_point_packets.append(Packet(id=str(x[0]) + ", " + str(x[1]),
point=weighted_properties,
position={"cartographicDegrees": [ x[1], x[0], 15 ]}))
with open("static/output.czml", "w") as file1:
if best_point and weighted_point != None:
file1.write(str(Document([top] + best_point_packets + weighted_point_packets + all_point_packets)))
elif best_point != None:
file1.write(str(Document([top] + best_point_packets + all_point_packets)))
else:
file1.write(str(Document([top] + all_point_packets)))
def Reverse(lst):
lst.reverse()
return lst
def clear():
def clear(debugging):
if not debugging:
# for windows
if name == 'nt':
_ = system('cls')
# for mac and linux(here, os.name is 'posix')
else:
_ = system('clear')
if name == 'nt':
_ = system('cls')
# for mac and linux(here, os.name is 'posix')
else:
_ = system('clear')
with open('accesstoken.txt', "r") as tokenfile:
access_token = tokenfile.read().replace('\n', '')
@route('/static/<filepath:path>', name='static')
def server_static(filepath):
return static_file(filepath, root='./static')
@get('/')
@get('/index')
@get('/cesium')
def cesium():
write_czml(*process_data(database_name, geofile, eps, min_samp))
return template('cesium.tpl',
{'access_token':access_token,
'epsilon':eps*100})
@post('/')
@post('/index')
@post('/cesium')
def update_cesium():
global eps
eps = float(request.forms.get('epsilonValue'))/100
print(eps)
return redirect('cesium')
def start_server(ipaddr = "127.0.0.1"):
run(host=ipaddr, port=8080, quiet=True, debug=False, server='paste')
if __name__ == '__main__':
# ipaddr = "127.0.0.1"
web = threading.Thread(target=start_server,)
web.daemon = True
web.start()
usage = "usage: %prog [options]"
parser = OptionParser(usage=usage)
parser.add_option("-g", "--geofile", dest="geofile", help="GeoJSON Output File", metavar="FILE")
......@@ -217,6 +316,8 @@ if __name__ == '__main__':
metavar="Number", type="int", default=20)
parser.add_option("--dist-from-reference", dest="mdfr", help="Max distance in km from intersection with strongest signal.",
metavar="Number", type="int", default=500)
parser.add_option("--debugging", dest="debugging", help="Does not clear the screen. Useful for seeing errors and warnings.",
action="store_true")
(options, args) = parser.parse_args()
mandatories = ['geofile', 'rx_file', 'database_name']
......@@ -234,13 +335,14 @@ if __name__ == '__main__':
min_power = options.pwr
max_dist_from_ref = options.mdfr
min_samp = options.minsamp
debugging = False if not options.debugging else True
try:
clear()
clear(debugging)
dots = 0
conn = sqlite3.connect(database_name)
c = conn.cursor()
c.execute("CREATE TABLE IF NOT EXISTS intersects (time INTEGER, latitude REAL, longitude REAL)")
c.execute("CREATE TABLE IF NOT EXISTS intersects (time INTEGER, latitude REAL, longitude REAL, num_parents INTEGER)")
receivers = []
with open(rx_file, "r") as file2:
......@@ -254,8 +356,11 @@ if __name__ == '__main__':
receiving = True
while receiving:
print("Receiving" + dots*'.')
print("Press Control+C to process data and exit.")
if not debugging:
print("Receiving" + dots*'.')
print("Press Control+C to process data and exit.")
intersect_list = np.array([]).reshape(0,3)
for x in range(len(receivers)):
for y in range(x):
......@@ -295,9 +400,9 @@ if __name__ == '__main__':
# print("Deleted Bad Intersection")
avg_coord = np.mean(intersect_list, axis = 0)
to_table = [receivers[x].doa_time, avg_coord[0], avg_coord[1]]
to_table = [receivers[x].doa_time, avg_coord[0], avg_coord[1], len(intersect_list)]
# print(to_table)
c.execute("INSERT INTO intersects VALUES (?,?,?)", to_table)
c.execute("INSERT INTO intersects VALUES (?,?,?,?)", to_table)
conn.commit()
for rx in receivers:
......@@ -307,13 +412,12 @@ if __name__ == '__main__':
dots = 1
else:
dots += 1
clear()
clear(debugging)
except KeyboardInterrupt:
clear()
print("Processing, please wait.")
# clear(debugging)
# print("Processing, please wait.")
conn.commit()
conn.close()
process_data(database_name, geofile, eps, min_samp)
quit()
#write_geojson(*process_data(database_name, geofile, eps, min_samp)[:2])
kill(getpid(), signal.SIGTERM)
body {
font-size: 12pt;
font-family: 'Courier New', Courier, monospace;
background-color: #ecebeb;
}
input {
margin: 3px;
}
h1, h2, h3, h4 {
margin-top: 10px;
margin-bottom: 5px;
}
p, ul {
margin-top: 5px;
margin-bottom: 5px;
}
span {
display: inline-block;
}
.header {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
/*.header li:last-child {
border-right: none;
border-left: 1px solid #bbb;
}*/
/* Change the link color to #111 (black) on hover */
.header a:hover {
background-color: #111;
}
.active {
background-color: #4CAF50;
}
.header {
font-size: 18pt;
font-family: 'Courier New', Courier, monospace;
}
.main_text {
width: 80%;
float: left;
padding: 10px;
}
.navbar {
overflow: hidden;
background-color: #333;
}
/*.header a {
display: block;
}*/
.navbar a {
float: left;
font-size: 18px;
color: white;
text-align: center;
padding: 17px 16px;
text-decoration: none;
border-left: 1px solid #bbb;
}
/* Dropdown Button */
.dropbtn {
background-color: #333;
color: white;
padding: 16px;
font-size: 18px;
border: none;
border-left: 1px solid #bbb;
font-family: 'Courier New', Courier, monospace;
}
/* The container <div> - needed to position the dropdown content */
.dropdown {
/*position: relative;*/
display: inline-block;
}
/* Dropdown Content (Hidden by Default) */
.dropdown-content {
display: none;
position: absolute;
background-color: #e4e1e1;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
/* Links inside the dropdown */
.dropdown-content a {
color: black;
padding: 12px 16px;
font-size: 16px;
text-decoration: none;
display: block;
}
/* Change color of dropdown links on hover */
.dropdown-content a:hover {background-color: #ccc;}
/* Show the dropdown menu on hover */
.dropdown:hover .dropdown-content {display: block;}
/* Change the background color of the dropdown button when the dropdown content is shown */
.dropdown:hover .dropbtn {background-color: #111;}
/*Slider Stuff*/
.slidecontainer {
width: 100%; /* Width of the outside container */
}
.slidespan {
width: 80%; /* Width of the outside container */
padding: 10px;
}
/* The slider itself */
.slider {
-webkit-appearance: none; /* Override default CSS styles */
appearance: none;
vertical-align: middle;
width: 100%;
height: 25px; /* Specified height */
background: #d3d3d3; /* Grey background */
outline: none; /* Remove outline */
opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */
-webkit-transition: .2s; /* 0.2 seconds transition on hover */
transition: opacity .2s;
}
/* Mouse-over effects */
.slider:hover {
opacity: 1; /* Fully shown on mouse-over */
}
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
.slider::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 25px; /* Set a specific slider handle width */
height: 25px; /* Slider handle height */
background: #333;
cursor: pointer; /* Cursor on hover */
}
.slider::-moz-range-thumb {
width: 25px; /* Set a specific slider handle width */
height: 25px; /* Slider handle height */
background: #333;
cursor: pointer; /* Cursor on hover */
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- Include the CesiumJS JavaScript and CSS files -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.75/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.75/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<link href="/static/style.css" rel="stylesheet">
</head>
<body>
<h2>DF Aggregator</h2>
<div id="cesiumContainer" style="height: 800px"></div>
<script>
// Your access token can be found at: https://cesium.com/ion/tokens.
Cesium.Ion.defaultAccessToken = '{{access_token}}';
var viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain()
});
var dataSourcePromise = Cesium.CzmlDataSource.load('/static/output.czml');
viewer.dataSources.add(dataSourcePromise);
viewer.zoomTo(dataSourcePromise);
// Add Cesium OSM Buildings, a global 3D buildings layer.
const buildingTileset = viewer.scene.primitives.add(Cesium.createOsmBuildings());
</script>
</div>
<div class="slidecontainer">
<form action="/" method="post">
<span><h4>epsilon:</h4></span>
<span class="slidespan"><input name="epsilonValue" type="range" min="0" max="100" value="{{epsilon}}" class="slider" id="epsilonRange"></span>
<span><p><input value="Update" type="submit" style="height:40px;"/></p></span>
</form>
<p>Value: <span id="epsilon"></span></p>
</div>
<script>
var slider = document.getElementById("epsilonRange");
var output = document.getElementById("epsilon");
output.innerHTML = slider.value/100; // Display the default slider value
// Update the current slider value (each time you drag the slider handle)
slider.oninput = function() {
output.innerHTML = this.value/100;
}
</script>
<h4>Buttons here in the future!</h4>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""></script>
</head>
<body>
<p>Test</p>
<div style="height: 700px" id="map"></div>
<script>
// initialize Leaflet
var map = L.map('map').setView({lon: -76.3, lat: 39.1}, 8);
// add the OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);
// show the scale bar on the lower left corner
L.control.scale().addTo(map);
</script>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment