mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-03-04 23:27:46 +01:00
235 lines
8.1 KiB
HTML
235 lines
8.1 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block css %}
|
|
/* Styles for the node info card */
|
|
#node_info {
|
|
height: 100%;
|
|
}
|
|
|
|
/* Styles for the map */
|
|
#map {
|
|
height: 100%;
|
|
min-height: 400px;
|
|
}
|
|
|
|
/* Styles for packet details section */
|
|
#packet_details {
|
|
height: 95vh;
|
|
overflow: scroll;
|
|
top: 3em;
|
|
}
|
|
|
|
/* Ensure inline display for details */
|
|
div.tab-pane > dl {
|
|
display: inline-block;
|
|
}
|
|
|
|
/* Set the maximum width of the page to 900px */
|
|
.container {
|
|
max-width: 900px;
|
|
margin: 0 auto; /* Center the content horizontally */
|
|
}
|
|
{% endblock %}
|
|
|
|
{% block body %}
|
|
<div id="node" class="container text-center">
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col mb-3">
|
|
<!-- Node Information Card -->
|
|
<div class="card" id="node_info">
|
|
{% if node %}
|
|
<div class="card-header" id="node_color">
|
|
<strong style="margin-right: 1em ; margin-left: 1em; font-size: x-large;">{{node.short_name}}</strong>
|
|
<p style="margin-bottom: 0px; font-size: large; font-weight: bold;">{{node.long_name}}</p>
|
|
</div>
|
|
<div class="card-body">
|
|
<dl >
|
|
{% if trace %}
|
|
<dd id="map"></dd>
|
|
{% endif %}
|
|
<dt>NodeID</dt>
|
|
<dd>{{node.node_id|node_id_to_hex}}</dd>
|
|
<dt>Channel</dt>
|
|
<dd>{{node.channel}}</dd>
|
|
<dt>HW Model</dt>
|
|
<dd>{{node.hw_model}}</dd>
|
|
<dt>Role</dt>
|
|
<dd>{{node.role}}</dd>
|
|
{% if node.firmware %}
|
|
<dt>Firmware</dt>
|
|
<dd>{{node.firmware}}</dd>
|
|
{% endif %}
|
|
</dl>
|
|
<a href="/top?node_id={{node.node_id}}" >Get node traffic totals</a>
|
|
{% include "node_graphs.html" %}
|
|
</div>
|
|
{% else %}
|
|
<div class="card-body">
|
|
A NodeInfo has not been seen.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col">
|
|
<!-- Additional buttons can be included here -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col">
|
|
{% include 'packet_list.html' %}
|
|
</div>
|
|
<!-- <div class="col sticky-top" id="packet_details"></div> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var node_color = document.getElementById('node_color');
|
|
var node_id = '{{node.node_id | node_id_to_hex}}';
|
|
var color = node_id.slice(-6);
|
|
var bg_color = "#"+color;
|
|
node_color.style.background = bg_color;
|
|
var hex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(bg_color);
|
|
var text_color = [
|
|
parseInt(hex[1], 16),
|
|
parseInt(hex[2], 16),
|
|
parseInt(hex[3], 16),
|
|
];
|
|
|
|
const brightness = Math.round(((parseInt(text_color[0]) * 299) +
|
|
(parseInt(text_color[1]) * 587) +
|
|
(parseInt(text_color[2]) * 114)) / 1000);
|
|
|
|
if (brightness > 125) {
|
|
var textColor = '#212529'
|
|
node_color.style.color = textColor
|
|
}
|
|
</script>
|
|
|
|
{% if trace %}
|
|
<script>
|
|
var trace = {{ trace | tojson }}; // Load trace data into JavaScript
|
|
var map = L.map('map').setView(trace[0], 13); // Initialize map centered at first trace point
|
|
var markers = L.featureGroup(); // Create a feature group for markers
|
|
markers.addTo(map);
|
|
|
|
// Add tile layer (OpenStreetMap)
|
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
}).addTo(map);
|
|
|
|
// Draw a polyline along the trace path
|
|
L.polyline(trace, { color: 'blue', weight: 1}).addTo(map);
|
|
|
|
// Add a red circle marker for the starting node with a tooltip
|
|
var startMarker = L.circleMarker(trace[0], {
|
|
radius: 8,
|
|
color: 'red',
|
|
weight: 1,
|
|
fillColor: 'red',
|
|
fillOpacity: 0.5
|
|
}).addTo(markers) // Add to feature group
|
|
.bindPopup(`
|
|
<b>{{node.long_name}}</b><br/>
|
|
<b>Short:</b> {{node.short_name}}<br/>
|
|
<b>Channel:</b> {{node.channel}}<br/>
|
|
<b>Hardware:</b> {{node.hw_model}}<br/>
|
|
<b>Role:</b> {{node.role}}<br/>
|
|
<b>Firmware:</b> {{node.firmware}}<br/>
|
|
<b>Coordinates:</b> [{{node.last_lat}} , {{node.last_long}}]
|
|
`, {permanent: false, direction: 'top', opacity: 0.9});
|
|
|
|
// Function to calculate distance and convert to miles
|
|
function getDistanceInMiles(latlng1, latlng2) {
|
|
var meters = latlng1.distanceTo(latlng2); // Get distance in meters
|
|
return meters * 0.000621371; // Convert meters to miles
|
|
}
|
|
|
|
{% for n in neighbors %}
|
|
var neighborLatLng = L.latLng([{{n.location[0]}}, {{n.location[1]}}]);
|
|
var startLatLng = L.latLng(trace[0]);
|
|
|
|
// Calculate distance in miles with 1 decimal place
|
|
var distanceMiles = getDistanceInMiles(startLatLng, neighborLatLng).toFixed(1);
|
|
|
|
// Create a blue circle marker for each neighbor node
|
|
var m = L.circleMarker(neighborLatLng, {
|
|
radius: 6,
|
|
color: 'blue',
|
|
weight: 1,
|
|
fillColor: 'blue',
|
|
fillOpacity: 0.5
|
|
}).addTo(markers) // Add to feature group
|
|
.bindPopup(`
|
|
<b><a href="/packet_list/{{n.node_id}}">{{n.long_name}}</a></b><br>
|
|
<b>{{n.short_name}}</b> <br/>
|
|
<b>SNR:</b> {{n.snr}} <br/>
|
|
<b>Distance:</b> ${distanceMiles} miles <br/>
|
|
`, {permanent: false, direction: 'top', opacity: 0.9});
|
|
|
|
// Draw a polyline from the first trace point to each neighbor node
|
|
L.polyline([startLatLng, neighborLatLng], {
|
|
color: 'grey',
|
|
weight: 1
|
|
}).addTo(map);
|
|
|
|
{% endfor %}
|
|
|
|
// Add a legend to the map
|
|
var legend = L.control({ position: 'bottomleft' });
|
|
|
|
legend.onAdd = function(map) {
|
|
var div = L.DomUtil.create('div', 'info legend');
|
|
div.style.background = 'white';
|
|
div.style.padding = '8px';
|
|
div.style.border = '1px solid black';
|
|
div.style.borderRadius = '5px';
|
|
div.style.boxShadow = '0 0 5px rgba(0,0,0,0.3)';
|
|
div.style.color = 'black'; // Ensure text is black
|
|
div.style.textAlign = 'left'; /* Ensure left alignment */
|
|
div.innerHTML = `
|
|
<b>Legend</b><br>
|
|
<svg width="16" height="16">
|
|
<circle cx="8" cy="8" r="6" fill="blue" stroke="blue" stroke-width="1" fill-opacity="0.4"/>
|
|
</svg> Neighbor Node<br>
|
|
<svg width="20" height="20">
|
|
<circle cx="10" cy="10" r="8" fill="red" stroke="red" stroke-width="1" fill-opacity="0.4"/>
|
|
</svg> Home Node<br>
|
|
<svg width="20" height="4">
|
|
<line x1="0" y1="2" x2="20" y2="2" stroke="grey" stroke-width="2"/>
|
|
</svg> Connection to Neighbors<br>
|
|
<svg width="20" height="4">
|
|
<line x1="0" y1="2" x2="20" y2="2" stroke="blue" stroke-width="2"/>
|
|
</svg> Path taken by node
|
|
`;
|
|
return div;
|
|
};
|
|
|
|
legend.addTo(map);
|
|
|
|
// Ensure the map adjusts to fit all markers and trace points
|
|
setTimeout(() => {
|
|
if (markers.getLayers().length > 0 || trace.length > 0) {
|
|
var bounds = markers.getBounds(); // Get bounds from markers
|
|
|
|
// Ensure trace points are included in the bounds
|
|
trace.forEach(point => {
|
|
bounds.extend(point);
|
|
});
|
|
|
|
map.fitBounds(bounds.pad(0.1), { maxZoom: 10 });
|
|
}
|
|
}, 200); // Slightly longer delay to ensure all elements are fully loaded
|
|
|
|
</script>
|
|
|
|
{% endif %}
|
|
|
|
{% endblock %}
|