mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-16 14:34:45 -06:00
First try at a bubble map.
This commit is contained in:
parent
280447f79d
commit
c6830abae6
@ -135,7 +135,7 @@ pub enum BusResponse {
|
||||
CurrentEndpointsByCountry(Vec<(String, [u64; 2], [f32; 2])>),
|
||||
|
||||
/// Current Lat/Lon of endpoints
|
||||
CurrentLatLon(Vec<(f64, f64)>),
|
||||
CurrentLatLon(Vec<(f64, f64, String, u64, f32)>),
|
||||
|
||||
/// Summary of Ether Protocol
|
||||
EtherProtocols{
|
||||
|
@ -60,7 +60,7 @@ pub async fn flows_by_country() -> NoCache<Json<Vec<(String, [u64; 2], [f32; 2])
|
||||
}
|
||||
|
||||
#[get("/api/flows/lat_lon")]
|
||||
pub async fn flows_lat_lon() -> NoCache<Json<Vec<(f64, f64)>>> {
|
||||
pub async fn flows_lat_lon() -> NoCache<Json<Vec<(f64, f64, String, u64, f32)>>> {
|
||||
let responses =
|
||||
bus_request(vec![BusRequest::CurrentEndpointLatLon]).await.unwrap();
|
||||
let result = match &responses[0] {
|
||||
|
@ -6,103 +6,146 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
|
||||
|
||||
|
||||
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
|
||||
|
||||
<script type="text/javascript"
|
||||
src="https://fastly.jsdelivr.net/npm/echarts@5.5.0/dist/extension/dataTool.min.js"></script>
|
||||
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts-gl@2/dist/echarts-gl.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="https://fastly.jsdelivr.net/npm/echarts-stat@latest/dist/ecStat.min.js"></script>
|
||||
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@4.9.0/map/js/china.js"></script>
|
||||
<script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@4.9.0/map/js/world.js"></script>
|
||||
<script src="/vendor/plotly-2.16.1.min.js"></script>
|
||||
<script src="/vendor/jquery.min.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body style="background: black">
|
||||
<div id="chart" style="width:100%; height: 700px; background: black;">
|
||||
<body style="background: black; font-family: Arial, Helvetica, sans-serif; height: 100%; margin: 0;">
|
||||
<p style="font-size: 20pt; text-align: center; color: #dddddd" id="heading"></p>
|
||||
<p style="font-size: 12pt; text-align: center; color: #dddddd">
|
||||
<a href="#" onclick="init()">Refresh</a>
|
||||
</p>
|
||||
<div id="chart" style="width:100vw; height: 100bh; background: black;">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var chartDom;
|
||||
var myChart;
|
||||
var option;
|
||||
var routes = [];
|
||||
|
||||
function lerpColor(color1, color2, weight) {
|
||||
var r = Math.round(color1[0] + (color2[0] - color1[0]) * weight);
|
||||
var g = Math.round(color1[1] + (color2[1] - color1[1]) * weight);
|
||||
var b = Math.round(color1[2] + (color2[2] - color1[2]) * weight);
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
}
|
||||
|
||||
function getColorForWeight(weight) {
|
||||
// Define our colors as [R, G, B]
|
||||
const green = [0, 128, 0];
|
||||
const orange = [255, 165, 0];
|
||||
const red = [255, 0, 0];
|
||||
|
||||
if (weight <= 0.5) {
|
||||
// Scale weight to be from 0 to 1 for the green to orange transition
|
||||
const adjustedWeight = weight * 2;
|
||||
return lerpColor(green, orange, adjustedWeight);
|
||||
} else {
|
||||
// Scale weight to be from 0 to 1 for the orange to red transition
|
||||
const adjustedWeight = (weight - 0.5) * 2;
|
||||
return lerpColor(orange, red, adjustedWeight);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
chartDom = document.getElementById('chart');
|
||||
myChart = echarts.init(chartDom);
|
||||
option;
|
||||
myChart.showLoading();
|
||||
|
||||
$.get("/api/flows/lat_lon", (data) => {
|
||||
for (var i=0; i<data.length; i++) {
|
||||
data[i] = [
|
||||
[data[i][1], data[i][0]],
|
||||
[-92.328636, 38.951561]
|
||||
];
|
||||
}
|
||||
routes = data;
|
||||
console.log(routes);
|
||||
myChart.hideLoading();
|
||||
myChart.setOption({
|
||||
geo3D: {
|
||||
map: 'world',
|
||||
shading: 'realistic',
|
||||
silent: true,
|
||||
environment: '#333',
|
||||
realisticMaterial: {
|
||||
roughness: 0.8,
|
||||
metalness: 0
|
||||
},
|
||||
postEffect: {
|
||||
enable: true
|
||||
},
|
||||
groundPlane: {
|
||||
show: false
|
||||
},
|
||||
light: {
|
||||
main: {
|
||||
intensity: 1,
|
||||
alpha: 30
|
||||
},
|
||||
ambient: {
|
||||
intensity: 0
|
||||
}
|
||||
},
|
||||
viewControl: {
|
||||
distance: 70,
|
||||
alpha: 89,
|
||||
panMouseButton: 'left',
|
||||
rotateMouseButton: 'right'
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#000'
|
||||
},
|
||||
regionHeight: 0.5
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'lines3D',
|
||||
coordinateSystem: 'geo3D',
|
||||
effect: {
|
||||
show: true,
|
||||
trailWidth: 1,
|
||||
trailOpacity: 0.5,
|
||||
trailLength: 0.2,
|
||||
constantSpeed: 5
|
||||
},
|
||||
blendMode: 'lighter',
|
||||
lineStyle: {
|
||||
width: 0.2,
|
||||
opacity: 0.05
|
||||
},
|
||||
data: routes
|
||||
$.get("/api/flows/lat_lon", (worldData) => {
|
||||
let condensed = {};
|
||||
let totalBytes = 0;
|
||||
for (let i = 0; i < worldData.length; i++) {
|
||||
let label = worldData[i][2];
|
||||
if (label in condensed) {
|
||||
condensed[label][3] += worldData[i][3]; // Bytes
|
||||
totalBytes += worldData[i][3];
|
||||
if (worldData[i][4] != 0) {
|
||||
condensed[label][4].push(worldData[i][4]); // RTT
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
condensed[label] = worldData[i];
|
||||
worldData[i][4] = [worldData[i][4]];
|
||||
totalBytes += worldData[i][3];
|
||||
}
|
||||
}
|
||||
|
||||
option && myChart.setOption(option);
|
||||
let entries = [];
|
||||
for (const [key, value] of Object.entries(condensed)) {
|
||||
value[3] = value[3] / totalBytes;
|
||||
entries.push(value);
|
||||
}
|
||||
|
||||
$("#heading").text("World Data. Now tracking " + worldData.length + " flows and " + entries.length + " locations.");
|
||||
|
||||
var data = [{
|
||||
type: 'scattergeo',
|
||||
//locationmode: 'world',
|
||||
lat: [],
|
||||
lon: [],
|
||||
hoverinfo: 'text',
|
||||
text: [],
|
||||
marker: {
|
||||
size: [],
|
||||
color: [],
|
||||
line: {
|
||||
color: [],
|
||||
width: 2
|
||||
},
|
||||
}
|
||||
}];
|
||||
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
var flow = entries[i];
|
||||
if (flow[4].length != 0) {
|
||||
var lat = flow[0];
|
||||
var lon = flow[1];
|
||||
var text = flow[2];
|
||||
|
||||
var bytes = flow[3] * 20;
|
||||
if (bytes < 5) bytes = 5;
|
||||
|
||||
let middle = flow[4].length -1;
|
||||
var rtt = flow[4][middle] / 1000000;
|
||||
rtt = rtt / 200;
|
||||
if (rtt > 1) rtt = 1;
|
||||
var color = getColorForWeight(rtt);
|
||||
data[0].lat.push(lat);
|
||||
data[0].lon.push(lon);
|
||||
data[0].text.push(text);
|
||||
data[0].marker.size.push(bytes);
|
||||
data[0].marker.line.color.push(color);
|
||||
data[0].marker.color.push(color);
|
||||
}
|
||||
}
|
||||
|
||||
var layout = {
|
||||
autosize: true,
|
||||
margin: {
|
||||
l: 0,
|
||||
r: 0,
|
||||
b: 0,
|
||||
t: 0,
|
||||
pad: 0
|
||||
},
|
||||
paper_bgcolor: 'black',
|
||||
geo: {
|
||||
scope: 'world',
|
||||
projection: {
|
||||
type: 'natural earth'
|
||||
},
|
||||
showland: true,
|
||||
showocean: true,
|
||||
showlakes: true,
|
||||
showrivers: true,
|
||||
showcountries: true,
|
||||
landcolor: 'rgb(217, 217, 217)',
|
||||
subunitwidth: 1,
|
||||
countrywidth: 1,
|
||||
subunitcolor: 'rgb(255,255,255)',
|
||||
countrycolor: 'rgb(255,255,255)',
|
||||
framecolor: 'black',
|
||||
bgcolor: 'black',
|
||||
},
|
||||
};
|
||||
|
||||
Plotly.newPlot('chart', data, layout, { responsive: true, displayModeBar: false });
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -36,17 +36,18 @@ impl TimeBuffer {
|
||||
buffer.push(entry);
|
||||
}
|
||||
|
||||
pub fn lat_lon_endpoints(&self) -> Vec<(f64, f64)> {
|
||||
pub fn lat_lon_endpoints(&self) -> Vec<(f64, f64, String, u64, f32)> {
|
||||
let buffer = self.buffer.lock().unwrap();
|
||||
let mut my_buffer = buffer
|
||||
.iter()
|
||||
.map(|v| {
|
||||
let (key, _data, _analysis) = &v.data;
|
||||
let (key, data, _analysis) = &v.data;
|
||||
let (lat, lon) = get_asn_lat_lon(key.remote_ip.as_ip());
|
||||
(lat, lon)
|
||||
let (_name, country) = get_asn_name_and_country(key.remote_ip.as_ip());
|
||||
(lat, lon, country, data.bytes_sent[1], data.rtt[1].as_nanos() as f32)
|
||||
})
|
||||
.filter(|(lat, lon)| *lat != 0.0 && *lon != 0.0)
|
||||
.collect::<Vec<(f64, f64)>>();
|
||||
.filter(|(lat, lon, ..)| *lat != 0.0 && *lon != 0.0)
|
||||
.collect::<Vec<(f64, f64, String, u64, f32)>>();
|
||||
|
||||
// Sort by lat/lon
|
||||
my_buffer.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
||||
|
Loading…
Reference in New Issue
Block a user