feat: routes /map /nav Leaflet GPS/USBL + Chart.js depth viewer
This commit is contained in:
50
viz/static/js/map.js
Normal file
50
viz/static/js/map.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const map = L.map('map').setView([43.17, 5.70], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© OSM contributors', maxZoom: 19
|
||||
}).addTo(map);
|
||||
|
||||
const status = document.getElementById('status');
|
||||
let trackLayer = null;
|
||||
|
||||
async function loadJobs() {
|
||||
const res = await fetch('/nav/api/jobs');
|
||||
const jobs = await res.json();
|
||||
const sel = document.getElementById('job-select');
|
||||
jobs.forEach(j => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = j.id;
|
||||
opt.textContent = 'job_' + j.id;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
const urlJob = new URLSearchParams(window.location.search).get('job');
|
||||
const target = urlJob ? parseInt(urlJob) : (jobs.length > 0 ? jobs[0].id : null);
|
||||
if (target) {
|
||||
sel.value = target;
|
||||
loadJob(target);
|
||||
} else {
|
||||
status.textContent = 'Aucun job disponible';
|
||||
}
|
||||
}
|
||||
|
||||
async function loadJob(jobId) {
|
||||
status.textContent = 'Chargement job_' + jobId + '...';
|
||||
const res = await fetch('/nav/api/job/' + jobId + '/nav');
|
||||
const d = await res.json();
|
||||
if (trackLayer) map.removeLayer(trackLayer);
|
||||
if (!d.track || !d.track.x.length) {
|
||||
status.textContent = 'Pas de données track pour job_' + jobId;
|
||||
return;
|
||||
}
|
||||
const cx = d.track.x.reduce((a,b)=>a+b,0)/d.track.x.length;
|
||||
const cy = d.track.y.reduce((a,b)=>a+b,0)/d.track.y.length;
|
||||
const pts = d.track.x.map((x,i) => [43.17 + (d.track.y[i]-cy)*0.000009, 5.70 + (x-cx)*0.000009]);
|
||||
trackLayer = L.polyline(pts, { color: '#4ade80', weight: 2 }).addTo(map);
|
||||
map.fitBounds(trackLayer.getBounds());
|
||||
status.textContent = 'job_' + jobId + ' — ' + d.n_poses + ' poses | PLY: ' + (d.ply_ready ? 'prêt' : 'non dispo');
|
||||
}
|
||||
|
||||
document.getElementById('job-select').addEventListener('change', e => {
|
||||
if (e.target.value) loadJob(parseInt(e.target.value));
|
||||
});
|
||||
|
||||
loadJobs();
|
||||
60
viz/static/js/nav_charts.js
Normal file
60
viz/static/js/nav_charts.js
Normal file
@@ -0,0 +1,60 @@
|
||||
let chartXY = null, chartZ = null;
|
||||
|
||||
function initCharts() {
|
||||
const base = {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
plugins: { legend: { labels: { color: '#888' } }, title: { display: true, color: '#ccc' } },
|
||||
scales: {
|
||||
x: { ticks: { color: '#666' }, grid: { color: '#1a2a1a' } },
|
||||
y: { ticks: { color: '#666' }, grid: { color: '#1a2a1a' } }
|
||||
}
|
||||
};
|
||||
chartXY = new Chart(document.getElementById('chart-xy'), {
|
||||
type: 'scatter',
|
||||
data: { datasets: [{ label: 'Track XY (m)', data: [], borderColor: '#4ade80',
|
||||
backgroundColor: 'rgba(74,222,128,0.3)', pointRadius: 1 }] },
|
||||
options: { ...base, plugins: { ...base.plugins,
|
||||
title: { display: true, text: 'Track XY lingbot (m, local)', color: '#ccc' } } }
|
||||
});
|
||||
chartZ = new Chart(document.getElementById('chart-z'), {
|
||||
type: 'line',
|
||||
data: { datasets: [{ label: 'Z camera (m)', data: [], borderColor: '#60a5fa',
|
||||
backgroundColor: 'transparent', pointRadius: 0, borderWidth: 1.5 }] },
|
||||
options: { ...base,
|
||||
plugins: { ...base.plugins,
|
||||
title: { display: true, text: 'Z camera / profondeur approx (m)', color: '#ccc' } },
|
||||
scales: { ...base.scales, y: { ...base.scales.y, reverse: true } } }
|
||||
});
|
||||
}
|
||||
|
||||
async function loadJobs() {
|
||||
const res = await fetch('/nav/api/jobs');
|
||||
const jobs = await res.json();
|
||||
const sel = document.getElementById('job-select');
|
||||
jobs.forEach(j => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = j.id;
|
||||
opt.textContent = 'job_' + j.id;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
const urlJob = new URLSearchParams(window.location.search).get('job');
|
||||
const target = urlJob ? parseInt(urlJob) : (jobs.length > 0 ? jobs[0].id : null);
|
||||
if (target) { sel.value = target; loadJob(target); }
|
||||
}
|
||||
|
||||
async function loadJob(jobId) {
|
||||
const res = await fetch('/nav/api/job/' + jobId + '/nav');
|
||||
const d = await res.json();
|
||||
if (!d.track) return;
|
||||
chartXY.data.datasets[0].data = d.track.x.map((x,i) => ({ x, y: d.track.y[i] }));
|
||||
chartXY.update();
|
||||
chartZ.data.datasets[0].data = d.track.z.map((z,i) => ({ x: i, y: z }));
|
||||
chartZ.update();
|
||||
}
|
||||
|
||||
document.getElementById('job-select').addEventListener('change', e => {
|
||||
if (e.target.value) loadJob(parseInt(e.target.value));
|
||||
});
|
||||
|
||||
initCharts();
|
||||
loadJobs();
|
||||
Reference in New Issue
Block a user