Incluimos lineas para localizar facilmente latitud (l) y Longitud (L)

This commit is contained in:
Luis Gutierrez Lopez committed 2026-11-05 15:24:29 +01:00
1 parent a7302fe78e
commit 3b19a11197
2 files changed
+128 -48

No files matched your search

+64 -24
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Regla Portland Plotter - Visor Pro Avanzado con Escala</title> <title>Regla Portland Plotter - Visor Pro Avanzado con Lectura de Coordenadas</title>
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style> <style>
@@ -32,7 +32,7 @@
<div> <div>
<h1 class="text-sm font-bold text-white tracking-wide flex items-center gap-2"> <h1 class="text-sm font-bold text-white tracking-wide flex items-center gap-2">
Regla Portland Plotter Regla Portland Plotter
<span class="text-[9px] bg-amber-500/20 text-amber-300 border border-amber-500/30 px-2 py-0.5 rounded-full font-mono">Modo Distancias & Escala</span> <span class="text-[9px] bg-amber-500/20 text-amber-300 border border-amber-500/30 px-2 py-0.5 rounded-full font-mono">Modo Coordenadas (l, L) & Escala</span>
</h1> </h1>
<p class="text-[10px] text-slate-400">Simulador Profesional de Alta Fidelidad</p> <p class="text-[10px] text-slate-400">Simulador Profesional de Alta Fidelidad</p>
</div> </div>
@@ -176,23 +176,22 @@
</button> </button>
</div> </div>
<div class="space-y-2.5 leading-relaxed"> <div class="space-y-2.5 leading-relaxed">
<p><strong>Calibrar Escala (Distancias):</strong></p> <p><strong>1. Lectura Directa de Coordenadas (l, L):</strong></p>
<ul class="list-disc list-inside space-y-1 text-slate-400 pl-2"> <ul class="list-disc list-inside space-y-1 text-slate-400 pl-2">
<li>Haz clic en <strong>"Marcar Escala".</strong></li> <li>Cada punto marcado ($A, B, C \dots$) proyecta automáticamente dos líneas finas de color continuo hasta los bordes del canvas.</li>
<li>Marca un primer punto sobre la escala de latitudes de la carta, luego un segundo punto exactamente a <strong>5 millas náuticas</strong> de distancia.</li> <li>Sigue la <strong>línea horizontal</strong> hacia el margen izquierdo/derecho para leer la Latitud ($l$) exacta.</li>
<li>Sigue la <strong>línea vertical</strong> hacia el margen superior/inferior para leer la Longitud ($L$) exacta.</li>
</ul> </ul>
<p><strong>Trazar Rumbo y Medir Distancias:</strong></p> <p><strong>2. Calibrar Escala (Distancias):</strong></p>
<ul class="list-disc list-inside space-y-1 text-slate-400 pl-2"> <ul class="list-disc list-inside space-y-1 text-slate-400 pl-2">
<li>Usa <strong>"Añadir Puntos Ruta"</strong> para trazar waypoints (A➞B➞C). Sobre cada línea de ruta se mostrará la distancia en millas náuticas.</li> <li>Haz clic en <strong>"Marcar Escala"</strong>.</li>
<li>Haz clic en el Punto 0 y luego en el Punto 1 a <strong>5 millas náuticas</strong> en la escala de latitud.</li>
</ul> </ul>
<p><strong>Averiguar Rumbo Verdadero (Rv):</strong></p> <p><strong>3. Medir o Trazar Rumbos y Rutas:</strong></p>
<ul class="list-disc list-inside space-y-1 text-slate-400 pl-2"> <ul class="list-disc list-inside space-y-1 text-slate-400 pl-2">
<li>Coloca el borde recto de la regla uniendo tu origen y tu destino. La <strong>Gran Flecha Azul</strong> debe apuntar en el sentido de la navegación.</li> <li>Con el botón <strong>"Añadir Puntos Ruta"</strong> marca tus waypoints ($A \rightarrow B \rightarrow C$). Se calculará automáticamente el Rumbo Verdadero ($Rv$) y la distancia entre puntos.</li>
<li>Gira el disco central de la brújula (Rosa de los Vientos) hasta que las líneas de su cuadrícula interna queden perfectamente paralelas a los <strong>meridianos (líneas Norte-Sur)</strong> y el <strong>Norte</strong> de la brújula apuntando al Norte de la carta.</li>
<li>Lee directamente en el transportador el <strong>Rumbo Verdadero (Rv)</strong> indicado por la marca de lectura fija.</li>
</ul> </ul>
</div> </div>
<div class="pt-2 flex justify-end"> <div class="pt-2 flex justify-end">
<button onclick="toggleHelpModal(false)" class="bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold px-4 py-1.5 rounded-lg text-xs"> <button onclick="toggleHelpModal(false)" class="bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold px-4 py-1.5 rounded-lg text-xs">
Entendido Entendido
@@ -214,13 +213,25 @@
zoom: 1.0, zoom: 1.0,
mode: 'MOVE', // 'MOVE', 'ADD_POINTS', 'SCALE_P0', 'SCALE_P1' mode: 'MOVE', // 'MOVE', 'ADD_POINTS', 'SCALE_P0', 'SCALE_P1'
points: [], points: [],
scalePoints: [], // Almacena [ {x, y} para P0, {x, y} para P1 ] base a zoom 1.0 scalePoints: [],
pixelsPerNauticalMileBase: null, // Píxeles por milla a zoom 1.0 pixelsPerNauticalMileBase: null,
customChartImg: null, customChartImg: null,
baseWidth: 1400, baseWidth: 1400,
baseHeight: 900 baseHeight: 900
}; };
// Paleta de colores vivos distintos para identificar las coordenadas (l, L) de cada punto
const pointColors = [
'#ef4444', // Rojo (A)
'#3b82f6', // Azul (B)
'#10b981', // Verde (C)
'#a855f7', // Púrpura (D)
'#ec4899', // Rosa (E)
'#f97316', // Naranja (F)
'#06b6d4', // Cian (G)
'#84cc16' // Lima (H)
];
const canvas = document.getElementById('plotterCanvas'); const canvas = document.getElementById('plotterCanvas');
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
const container = document.getElementById('canvas-container'); const container = document.getElementById('canvas-container');
@@ -270,7 +281,7 @@
const scale = img.width > maxW ? maxW / img.width : 1; const scale = img.width > maxW ? maxW / img.width : 1;
state.baseWidth = img.width * scale; state.baseWidth = img.width * scale;
state.baseHeight = img.height * scale; state.baseHeight = img.height * scale;
state.pixelsPerNauticalMileBase = null; // Reiniciar escala al cambiar de carta state.pixelsPerNauticalMileBase = null;
updateScaleStatusUI(); updateScaleStatusUI();
initCanvasSize(); initCanvasSize();
centerRulerAbsolute(); centerRulerAbsolute();
@@ -362,7 +373,7 @@
ctx.setLineDash([]); ctx.setLineDash([]);
} }
// Dibujar puntos y líneas de calibración de escala si existen // Dibujar puntos y líneas de calibración de escala
if (state.scalePoints.length > 0) { if (state.scalePoints.length > 0) {
state.scalePoints.forEach((sp, idx) => { state.scalePoints.forEach((sp, idx) => {
let currX = sp.x * state.zoom; let currX = sp.x * state.zoom;
@@ -394,6 +405,29 @@
} }
} }
// --- LÍNEAS FINAS DE COORDENADAS (LATITUD Y LONGITUD) PARA CADA PUNTO ---
state.points.forEach((p, idx) => {
const color = pointColors[idx % pointColors.length];
ctx.strokeStyle = color;
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]); // Líneas discontinuas finas y elegantes
// Línea Horizontal (Latitud - l) a través de todo el Canvas
ctx.beginPath();
ctx.moveTo(0, p.y);
ctx.lineTo(canvas.width, p.y);
ctx.stroke();
// Línea Vertical (Longitud - L) a través de todo el Canvas
ctx.beginPath();
ctx.moveTo(p.x, 0);
ctx.lineTo(p.x, canvas.height);
ctx.stroke();
ctx.setLineDash([]);
});
// Dibujar líneas de ruta encadenadas (A -> B -> C -> ...) y sus distancias // Dibujar líneas de ruta encadenadas (A -> B -> C -> ...) y sus distancias
if (state.points.length > 1) { if (state.points.length > 1) {
ctx.strokeStyle = '#f59e0b'; ctx.strokeStyle = '#f59e0b';
@@ -407,7 +441,7 @@
ctx.stroke(); ctx.stroke();
ctx.setLineDash([]); ctx.setLineDash([]);
// Escribir la distancia en millas náuticas sobre cada segmento de ruta si la escala está calibrada // Escribir la distancia en millas náuticas sobre cada segmento
if (state.pixelsPerNauticalMileBase !== null) { if (state.pixelsPerNauticalMileBase !== null) {
let currentPixelsPerNM = state.pixelsPerNauticalMileBase * state.zoom; let currentPixelsPerNM = state.pixelsPerNauticalMileBase * state.zoom;
for (let i = 0; i < state.points.length - 1; i++) { for (let i = 0; i < state.points.length - 1; i++) {
@@ -440,9 +474,11 @@
} }
} }
// Dibujar marcadores de puntos de ruta // Dibujar marcadores de puntos de ruta con el color correspondiente
state.points.forEach(p => { state.points.forEach((p, idx) => {
ctx.fillStyle = '#ef4444'; const color = pointColors[idx % pointColors.length];
ctx.fillStyle = color;
ctx.beginPath(); ctx.beginPath();
ctx.arc(p.x, p.y, 6, 0, Math.PI * 2); ctx.arc(p.x, p.y, 6, 0, Math.PI * 2);
ctx.fill(); ctx.fill();
@@ -450,9 +486,13 @@
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.stroke(); ctx.stroke();
ctx.fillStyle = '#0f172a'; // Fondo para mejor legibilidad del nombre del punto
ctx.fillStyle = 'rgba(15, 23, 42, 0.75)';
ctx.fillRect(p.x + 8, p.y - 12, 55, 16);
ctx.fillStyle = color;
ctx.font = 'bold 11px sans-serif'; ctx.font = 'bold 11px sans-serif';
ctx.fillText(`Punto ${p.label}`, p.x + 10, p.y + 4); ctx.fillText(`Punto ${p.label}`, p.x + 12, p.y);
}); });
} }
@@ -729,11 +769,10 @@
return true; return true;
} else if (state.mode === 'SCALE_P1') { } else if (state.mode === 'SCALE_P1') {
state.scalePoints.push({ x: clickX / state.zoom, y: clickY / state.zoom }); state.scalePoints.push({ x: clickX / state.zoom, y: clickY / state.zoom });
// Calcular píxeles por milla a zoom 1.0
let p0 = state.scalePoints[0]; let p0 = state.scalePoints[0];
let p1 = state.scalePoints[1]; let p1 = state.scalePoints[1];
let distPixelsBase = Math.hypot(p1.x - p0.x, p1.y - p0.y); let distPixelsBase = Math.hypot(p1.x - p0.x, p1.y - p0.y);
state.pixelsPerNauticalMileBase = distPixelsBase / 5.0; // 5 millas náuticas state.pixelsPerNauticalMileBase = distPixelsBase / 5.0;
updateScaleStatusUI(); updateScaleStatusUI();
resetToMoveMode(); resetToMoveMode();
draw(); draw();
@@ -855,3 +894,4 @@
</body> </body>
</html> </html>
+64 -24
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Regla Portland Plotter - Visor Pro Avanzado con Escala</title> <title>Regla Portland Plotter - Visor Pro Avanzado con Lectura de Coordenadas</title>
<script src="assets/js/tailwindcss.js"></script> <script src="assets/js/tailwindcss.js"></script>
<link rel="stylesheet" href="assets/css/fontawesome.all.min.css"> <link rel="stylesheet" href="assets/css/fontawesome.all.min.css">
<style> <style>
@@ -32,7 +32,7 @@
<div> <div>
<h1 class="text-sm font-bold text-white tracking-wide flex items-center gap-2"> <h1 class="text-sm font-bold text-white tracking-wide flex items-center gap-2">
Regla Portland Plotter Regla Portland Plotter
<span class="text-[9px] bg-amber-500/20 text-amber-300 border border-amber-500/30 px-2 py-0.5 rounded-full font-mono">Modo Distancias & Escala</span> <span class="text-[9px] bg-amber-500/20 text-amber-300 border border-amber-500/30 px-2 py-0.5 rounded-full font-mono">Modo Coordenadas (l, L) & Escala</span>
</h1> </h1>
<p class="text-[10px] text-slate-400">Simulador Profesional de Alta Fidelidad</p> <p class="text-[10px] text-slate-400">Simulador Profesional de Alta Fidelidad</p>
</div> </div>
@@ -176,23 +176,22 @@
</button> </button>
</div> </div>
<div class="space-y-2.5 leading-relaxed"> <div class="space-y-2.5 leading-relaxed">
<p><strong>Calibrar Escala (Distancias):</strong></p> <p><strong>1. Lectura Directa de Coordenadas (l, L):</strong></p>
<ul class="list-disc list-inside space-y-1 text-slate-400 pl-2"> <ul class="list-disc list-inside space-y-1 text-slate-400 pl-2">
<li>Haz clic en <strong>"Marcar Escala".</strong></li> <li>Cada punto marcado ($A, B, C \dots$) proyecta automáticamente dos líneas finas de color continuo hasta los bordes del canvas.</li>
<li>Marca un primer punto sobre la escala de latitudes de la carta, luego un segundo punto exactamente a <strong>5 millas náuticas</strong> de distancia.</li> <li>Sigue la <strong>línea horizontal</strong> hacia el margen izquierdo/derecho para leer la Latitud ($l$) exacta.</li>
<li>Sigue la <strong>línea vertical</strong> hacia el margen superior/inferior para leer la Longitud ($L$) exacta.</li>
</ul> </ul>
<p><strong>Trazar Rumbo y Medir Distancias:</strong></p> <p><strong>2. Calibrar Escala (Distancias):</strong></p>
<ul class="list-disc list-inside space-y-1 text-slate-400 pl-2"> <ul class="list-disc list-inside space-y-1 text-slate-400 pl-2">
<li>Usa <strong>"Añadir Puntos Ruta"</strong> para trazar waypoints (A➞B➞C). Sobre cada línea de ruta se mostrará la distancia en millas náuticas.</li> <li>Haz clic en <strong>"Marcar Escala"</strong>.</li>
<li>Haz clic en el Punto 0 y luego en el Punto 1 a <strong>5 millas náuticas</strong> en la escala de latitud.</li>
</ul> </ul>
<p><strong>Averiguar Rumbo Verdadero (Rv):</strong></p> <p><strong>3. Medir o Trazar Rumbos y Rutas:</strong></p>
<ul class="list-disc list-inside space-y-1 text-slate-400 pl-2"> <ul class="list-disc list-inside space-y-1 text-slate-400 pl-2">
<li>Coloca el borde recto de la regla uniendo tu origen y tu destino. La <strong>Gran Flecha Azul</strong> debe apuntar en el sentido de la navegación.</li> <li>Con el botón <strong>"Añadir Puntos Ruta"</strong> marca tus waypoints ($A \rightarrow B \rightarrow C$). Se calculará automáticamente el Rumbo Verdadero ($Rv$) y la distancia entre puntos.</li>
<li>Gira el disco central de la brújula (Rosa de los Vientos) hasta que las líneas de su cuadrícula interna queden perfectamente paralelas a los <strong>meridianos (líneas Norte-Sur)</strong> y el <strong>Norte</strong> de la brújula apuntando al Norte de la carta.</li>
<li>Lee directamente en el transportador el <strong>Rumbo Verdadero (Rv)</strong> indicado por la marca de lectura fija.</li>
</ul> </ul>
</div> </div>
<div class="pt-2 flex justify-end"> <div class="pt-2 flex justify-end">
<button onclick="toggleHelpModal(false)" class="bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold px-4 py-1.5 rounded-lg text-xs"> <button onclick="toggleHelpModal(false)" class="bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold px-4 py-1.5 rounded-lg text-xs">
Entendido Entendido
@@ -214,13 +213,25 @@
zoom: 1.0, zoom: 1.0,
mode: 'MOVE', // 'MOVE', 'ADD_POINTS', 'SCALE_P0', 'SCALE_P1' mode: 'MOVE', // 'MOVE', 'ADD_POINTS', 'SCALE_P0', 'SCALE_P1'
points: [], points: [],
scalePoints: [], // Almacena [ {x, y} para P0, {x, y} para P1 ] base a zoom 1.0 scalePoints: [],
pixelsPerNauticalMileBase: null, // Píxeles por milla a zoom 1.0 pixelsPerNauticalMileBase: null,
customChartImg: null, customChartImg: null,
baseWidth: 1400, baseWidth: 1400,
baseHeight: 900 baseHeight: 900
}; };
// Paleta de colores vivos distintos para identificar las coordenadas (l, L) de cada punto
const pointColors = [
'#ef4444', // Rojo (A)
'#3b82f6', // Azul (B)
'#10b981', // Verde (C)
'#a855f7', // Púrpura (D)
'#ec4899', // Rosa (E)
'#f97316', // Naranja (F)
'#06b6d4', // Cian (G)
'#84cc16' // Lima (H)
];
const canvas = document.getElementById('plotterCanvas'); const canvas = document.getElementById('plotterCanvas');
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
const container = document.getElementById('canvas-container'); const container = document.getElementById('canvas-container');
@@ -270,7 +281,7 @@
const scale = img.width > maxW ? maxW / img.width : 1; const scale = img.width > maxW ? maxW / img.width : 1;
state.baseWidth = img.width * scale; state.baseWidth = img.width * scale;
state.baseHeight = img.height * scale; state.baseHeight = img.height * scale;
state.pixelsPerNauticalMileBase = null; // Reiniciar escala al cambiar de carta state.pixelsPerNauticalMileBase = null;
updateScaleStatusUI(); updateScaleStatusUI();
initCanvasSize(); initCanvasSize();
centerRulerAbsolute(); centerRulerAbsolute();
@@ -362,7 +373,7 @@
ctx.setLineDash([]); ctx.setLineDash([]);
} }
// Dibujar puntos y líneas de calibración de escala si existen // Dibujar puntos y líneas de calibración de escala
if (state.scalePoints.length > 0) { if (state.scalePoints.length > 0) {
state.scalePoints.forEach((sp, idx) => { state.scalePoints.forEach((sp, idx) => {
let currX = sp.x * state.zoom; let currX = sp.x * state.zoom;
@@ -394,6 +405,29 @@
} }
} }
// --- LÍNEAS FINAS DE COORDENADAS (LATITUD Y LONGITUD) PARA CADA PUNTO ---
state.points.forEach((p, idx) => {
const color = pointColors[idx % pointColors.length];
ctx.strokeStyle = color;
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]); // Líneas discontinuas finas y elegantes
// Línea Horizontal (Latitud - l) a través de todo el Canvas
ctx.beginPath();
ctx.moveTo(0, p.y);
ctx.lineTo(canvas.width, p.y);
ctx.stroke();
// Línea Vertical (Longitud - L) a través de todo el Canvas
ctx.beginPath();
ctx.moveTo(p.x, 0);
ctx.lineTo(p.x, canvas.height);
ctx.stroke();
ctx.setLineDash([]);
});
// Dibujar líneas de ruta encadenadas (A -> B -> C -> ...) y sus distancias // Dibujar líneas de ruta encadenadas (A -> B -> C -> ...) y sus distancias
if (state.points.length > 1) { if (state.points.length > 1) {
ctx.strokeStyle = '#f59e0b'; ctx.strokeStyle = '#f59e0b';
@@ -407,7 +441,7 @@
ctx.stroke(); ctx.stroke();
ctx.setLineDash([]); ctx.setLineDash([]);
// Escribir la distancia en millas náuticas sobre cada segmento de ruta si la escala está calibrada // Escribir la distancia en millas náuticas sobre cada segmento
if (state.pixelsPerNauticalMileBase !== null) { if (state.pixelsPerNauticalMileBase !== null) {
let currentPixelsPerNM = state.pixelsPerNauticalMileBase * state.zoom; let currentPixelsPerNM = state.pixelsPerNauticalMileBase * state.zoom;
for (let i = 0; i < state.points.length - 1; i++) { for (let i = 0; i < state.points.length - 1; i++) {
@@ -440,9 +474,11 @@
} }
} }
// Dibujar marcadores de puntos de ruta // Dibujar marcadores de puntos de ruta con el color correspondiente
state.points.forEach(p => { state.points.forEach((p, idx) => {
ctx.fillStyle = '#ef4444'; const color = pointColors[idx % pointColors.length];
ctx.fillStyle = color;
ctx.beginPath(); ctx.beginPath();
ctx.arc(p.x, p.y, 6, 0, Math.PI * 2); ctx.arc(p.x, p.y, 6, 0, Math.PI * 2);
ctx.fill(); ctx.fill();
@@ -450,9 +486,13 @@
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.stroke(); ctx.stroke();
ctx.fillStyle = '#0f172a'; // Fondo para mejor legibilidad del nombre del punto
ctx.fillStyle = 'rgba(15, 23, 42, 0.75)';
ctx.fillRect(p.x + 8, p.y - 12, 55, 16);
ctx.fillStyle = color;
ctx.font = 'bold 11px sans-serif'; ctx.font = 'bold 11px sans-serif';
ctx.fillText(`Punto ${p.label}`, p.x + 10, p.y + 4); ctx.fillText(`Punto ${p.label}`, p.x + 12, p.y);
}); });
} }
@@ -729,11 +769,10 @@
return true; return true;
} else if (state.mode === 'SCALE_P1') { } else if (state.mode === 'SCALE_P1') {
state.scalePoints.push({ x: clickX / state.zoom, y: clickY / state.zoom }); state.scalePoints.push({ x: clickX / state.zoom, y: clickY / state.zoom });
// Calcular píxeles por milla a zoom 1.0
let p0 = state.scalePoints[0]; let p0 = state.scalePoints[0];
let p1 = state.scalePoints[1]; let p1 = state.scalePoints[1];
let distPixelsBase = Math.hypot(p1.x - p0.x, p1.y - p0.y); let distPixelsBase = Math.hypot(p1.x - p0.x, p1.y - p0.y);
state.pixelsPerNauticalMileBase = distPixelsBase / 5.0; // 5 millas náuticas state.pixelsPerNauticalMileBase = distPixelsBase / 5.0;
updateScaleStatusUI(); updateScaleStatusUI();
resetToMoveMode(); resetToMoveMode();
draw(); draw();
@@ -855,3 +894,4 @@
</body> </body>
</html> </html>