Rediseñar símbolos BCNLAT y LIGHTS + land Barranquilla
BCNLAT (_encBeaconCanvas): rectángulo delgado y alto en color IALA. Babor (puerto, verde IALA-B): tope plano = rectángulo puro. Estribor (estribor, rojo IALA-B): tope triangular apuntando arriba + cuerpo rectangular debajo. Gradiente lateral 3-D. Aplica igual a daymarks del ICW Miami y balizas de orilla Colombia. LIGHTS blanco (_ialaLight): rectángulo cuerpo blanco borde negro, estrella de 5 puntas centrada, lagrima estirada purpura a ~20° de la vertical saliendo del costado superior del rectángulo (S-52 style). land.geojson BARRANQUILLA: polígono único trazando la costa desde Galerazamba (O) hasta la boca del canal Bocas de Ceniza (E), incluyendo ambas riberas del canal y la costa sur hasta el límite de la carta. Corrige tierra en 0 features. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
+116
-111
@@ -988,44 +988,83 @@ function _ialaBoySpp() {
|
||||
return c;
|
||||
}
|
||||
|
||||
// ── Light symbol — slim vertical post with a teardrop flame on top.
|
||||
// Colour follows the emitted light: white, red, green, yellow.
|
||||
// ── White-light structure symbol (LIGHTS blanco / faro de orilla) ─────────────
|
||||
// Diseño: rectángulo cuerpo blanco con borde negro + estrella de 5 puntas centrada
|
||||
// + lagrima estirada de color purpura que sale del costado superior hacia arriba
|
||||
// a ~20° de la vertical (convención ECDIS / S-52 adaptada).
|
||||
// Para luces de color (rojo/verde) se usa el símbolo de cuerpo de boya en _encLightCanvas.
|
||||
function _ialaLight(colourCode) {
|
||||
const W = 14, H = 34;
|
||||
const W = 26, H = 46;
|
||||
const c = document.createElement('canvas');
|
||||
c.width = W; c.height = H;
|
||||
const ctx = c.getContext('2d');
|
||||
const cx = W / 2;
|
||||
|
||||
// Sólo se invoca para luces blancas (kind==='flare'); defensivamente mapeamos colores.
|
||||
let col = '#ffffff';
|
||||
if (colourCode === 3) col = '#dd0000';
|
||||
if (colourCode === 3) col = '#dd1111';
|
||||
else if (colourCode === 4) col = '#00aa00';
|
||||
else if (colourCode === 6) col = '#ffcc00';
|
||||
|
||||
const cx = W / 2;
|
||||
// ── Cuerpo rectangulo (estructura del faro) ───────────────────────────────
|
||||
const bW = 14, bH = 17;
|
||||
const bTop = H * 0.50; // tope del rectángulo
|
||||
const bBot = bTop + bH; // base del rectángulo
|
||||
|
||||
// Slim vertical post (the structure)
|
||||
ctx.fillStyle = col;
|
||||
ctx.strokeStyle = '#111';
|
||||
ctx.lineWidth = 0.8;
|
||||
ctx.fillRect(cx - 1.25, 12, 2.5, 18);
|
||||
ctx.strokeRect(cx - 1.25, 12, 2.5, 18);
|
||||
|
||||
// Teardrop flame on top (apex up, rounded base sitting on the post)
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx, 1);
|
||||
ctx.bezierCurveTo(cx + 4.5, 3.5, cx + 4.0, 9, cx, 11.5);
|
||||
ctx.bezierCurveTo(cx - 4.0, 9, cx - 4.5, 3.5, cx, 1);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = col;
|
||||
ctx.rect(cx - bW / 2, bTop, bW, bH);
|
||||
ctx.fillStyle = col; // blanco (o color para casos defensivos)
|
||||
ctx.fill();
|
||||
ctx.lineWidth = 0.8;
|
||||
ctx.strokeStyle = '#111';
|
||||
ctx.lineWidth = 1.2;
|
||||
ctx.stroke();
|
||||
|
||||
// Position dot at the bottom (geo anchor)
|
||||
// ── Estrella de 5 puntas centrada en el rectángulo ────────────────────────
|
||||
const starX = cx, starY = bTop + bH / 2;
|
||||
const Ro = 4.5, Ri = 1.85; // radio exterior / interior
|
||||
ctx.beginPath();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const ang = (i * Math.PI / 5) - Math.PI / 2; // empieza arriba
|
||||
const rr = (i % 2 === 0) ? Ro : Ri;
|
||||
const px = starX + Math.cos(ang) * rr;
|
||||
const py = starY + Math.sin(ang) * rr;
|
||||
i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = (col === '#ffffff') ? '#222' : '#fff'; // contraste
|
||||
ctx.fill();
|
||||
|
||||
// ── Lagrima estirada purpura (haz de luz) ─────────────────────────────────
|
||||
// Sale del costado superior-derecho del rectángulo, ~20° de la vertical.
|
||||
const PURP = '#8800cc';
|
||||
const tearAng = 20 * Math.PI / 180; // 20° desde la vertical
|
||||
const tearLen = 18; // longitud de la lagrima (px)
|
||||
|
||||
ctx.save();
|
||||
// Ancla en el extremo superior-derecho del rectángulo
|
||||
ctx.translate(cx + bW / 2 - 1, bTop + 1);
|
||||
ctx.rotate(tearAng); // rotar 20° hacia la derecha
|
||||
|
||||
// Forma de lagrima: estrecha en la base, ancha a la mitad, punta arriba
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0); // base (anclaje)
|
||||
ctx.bezierCurveTo(-3.8, -5, -3.8, -11, 0, -tearLen);// lado izquierdo
|
||||
ctx.bezierCurveTo( 3.8, -11, 3.8, -5, 0, 0); // lado derecho
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = PURP;
|
||||
ctx.globalAlpha = 0.88;
|
||||
ctx.fill();
|
||||
ctx.globalAlpha = 1.0;
|
||||
ctx.strokeStyle = 'rgba(90,0,140,0.5)';
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.stroke();
|
||||
|
||||
ctx.restore();
|
||||
|
||||
// ── Punto de anclaje geográfico en la base ────────────────────────────────
|
||||
ctx.beginPath(); ctx.arc(cx, H - 2, 2.2, 0, Math.PI * 2);
|
||||
ctx.fillStyle = '#fff'; ctx.fill();
|
||||
ctx.beginPath(); ctx.arc(cx, H - 2, 1, 0, Math.PI * 2);
|
||||
ctx.beginPath(); ctx.arc(cx, H - 2, 1.0, 0, Math.PI * 2);
|
||||
ctx.fillStyle = '#111'; ctx.fill();
|
||||
|
||||
return c;
|
||||
@@ -1107,109 +1146,75 @@ function _encLightCanvas(colours, region, sz = 32) {
|
||||
return c;
|
||||
}
|
||||
|
||||
// ── Lateral beacon (BCNLAT) — 3-D tripod + pole + topmark ──────────────────
|
||||
// Farillos de orilla: estructura fija (no flota). Tríangulo de patas + mástil
|
||||
// vertical + marca de tope cuadrada (babor) o cónica (estribor).
|
||||
function _encBeaconCanvas(colours, catlam, region, sz = 44) {
|
||||
// ── Lateral beacon (BCNLAT) — rectángulo delgado alto + tope cuadrado/triangular ──
|
||||
// Símbolo: cuerpo principal es un rectángulo delgado y alto en el color IALA lateral.
|
||||
// Babor (port, IALA-B verde): tope plano → rectángulo puro.
|
||||
// Estribor (stbd, IALA-B rojo): tope triangular apuntando hacia arriba.
|
||||
// Claramente distinguible de boyas flotantes. Funciona igual para faros de orilla
|
||||
// colombianos y daymarks del ICW de Florida.
|
||||
function _encBeaconCanvas(colours, catlam, region, sz = 36) {
|
||||
const c = _mkC(sz); const ctx = c.getContext('2d'); const cx = sz / 2;
|
||||
let cc = (colours || []).slice();
|
||||
if (!cc.length && catlam) cc = _ialaLateralColours(catlam, region);
|
||||
const col = cc[0] ? _s57css(cc[0]) : '#78909c';
|
||||
const isPort = catlam === 1 || catlam === 3;
|
||||
const isPort = catlam === 1 || catlam === 3; // IALA-B: babor=verde, estribor=rojo
|
||||
|
||||
// Layout
|
||||
const groundY = sz * 0.92; // ground level (shadow base)
|
||||
const legHub = sz * 0.72; // where tripod legs converge
|
||||
const poleBot = legHub;
|
||||
const poleTop = sz * 0.32; // top of mast
|
||||
const tmH = sz * 0.18; // topmark height
|
||||
const tmW = sz * 0.46; // topmark width
|
||||
const tmY = poleTop; // topmark sits at poleTop
|
||||
// ── Dimensiones del cuerpo ────────────────────────────────────────────────
|
||||
const bW = sz * 0.30; // ancho del cuerpo (delgado)
|
||||
const bTop = sz * 0.07; // tope del cuerpo
|
||||
const bBot = sz * 0.90; // base (nivel del suelo)
|
||||
const triH = bW * 1.05; // altura del triángulo = ≈ ancho (proporcional)
|
||||
const bodyTop = isPort ? bTop : bTop + triH; // tope del rectángulo (debajo del tri)
|
||||
|
||||
// ── Subtle glow ──────────────────────────────────────────────────────────
|
||||
const glow = ctx.createRadialGradient(cx, sz * 0.60, 0, cx, sz * 0.60, sz * 0.44);
|
||||
glow.addColorStop(0, 'rgba(255,255,255,0.0)');
|
||||
glow.addColorStop(0.65, 'rgba(180,210,255,0.10)');
|
||||
glow.addColorStop(1, 'rgba(100,160,255,0.20)');
|
||||
ctx.fillStyle = glow; ctx.fillRect(0, 0, sz, sz);
|
||||
// ── Sombra en la base ─────────────────────────────────────────────────────
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, bBot, sz * 0.12, sz * 0.022, 0, 0, Math.PI * 2);
|
||||
ctx.fillStyle = 'rgba(0,0,0,0.22)';
|
||||
ctx.fill();
|
||||
|
||||
// ── Ground shadow ─────────────────────────────────────────────────────────
|
||||
ctx.beginPath(); ctx.ellipse(cx, groundY, sz * 0.22, sz * 0.04, 0, 0, Math.PI * 2);
|
||||
ctx.fillStyle = 'rgba(0,0,0,0.22)'; ctx.fill();
|
||||
// ── Gradiente lateral (efecto 3-D suave) ─────────────────────────────────
|
||||
const grd = ctx.createLinearGradient(cx - bW / 2, 0, cx + bW / 2, 0);
|
||||
grd.addColorStop(0, _lighten3D(col, 0.35));
|
||||
grd.addColorStop(0.45, col);
|
||||
grd.addColorStop(1, _darken3D(col, 0.28));
|
||||
|
||||
// ── Tripod legs (3 legs, perspective depth) ───────────────────────────────
|
||||
// Back leg (darker, stub)
|
||||
ctx.beginPath(); ctx.moveTo(cx, legHub); ctx.lineTo(cx + sz*0.07, groundY * 0.97);
|
||||
ctx.strokeStyle = _darken3D(col, 0.50); ctx.lineWidth = 1.2; ctx.stroke();
|
||||
// Left leg
|
||||
ctx.beginPath(); ctx.moveTo(cx, legHub); ctx.lineTo(cx - sz*0.28, groundY);
|
||||
ctx.strokeStyle = _darken3D(col, 0.25); ctx.lineWidth = 1.4; ctx.stroke();
|
||||
ctx.strokeStyle = _lighten3D(col, 0.15); ctx.lineWidth = 0.5; ctx.stroke();
|
||||
// Right leg
|
||||
ctx.beginPath(); ctx.moveTo(cx, legHub); ctx.lineTo(cx + sz*0.28, groundY);
|
||||
ctx.strokeStyle = _darken3D(col, 0.30); ctx.lineWidth = 1.4; ctx.stroke();
|
||||
ctx.strokeStyle = _lighten3D(col, 0.10); ctx.lineWidth = 0.5; ctx.stroke();
|
||||
ctx.strokeStyle = 'rgba(0,0,0,0.65)';
|
||||
ctx.lineWidth = 0.9;
|
||||
|
||||
// ── Hub ring where legs meet ──────────────────────────────────────────────
|
||||
const hubGrd = ctx.createRadialGradient(cx - sz*0.05, legHub - sz*0.02, 1, cx, legHub, sz*0.11);
|
||||
hubGrd.addColorStop(0, _lighten3D(col, 0.45));
|
||||
hubGrd.addColorStop(1, _darken3D(col, 0.30));
|
||||
ctx.beginPath(); ctx.ellipse(cx, legHub, sz*0.11, sz*0.038, 0, 0, Math.PI*2);
|
||||
ctx.fillStyle = hubGrd; ctx.fill();
|
||||
ctx.strokeStyle = '#333'; ctx.lineWidth = 0.5; ctx.stroke();
|
||||
|
||||
// ── Mast / pole (thin cylinder with 3-D shading) ─────────────────────────
|
||||
const pw = sz * 0.07;
|
||||
const mastGrd = ctx.createLinearGradient(cx - pw/2, 0, cx + pw/2, 0);
|
||||
mastGrd.addColorStop(0, _darken3D('#888', 0.15));
|
||||
mastGrd.addColorStop(0.35, '#cccccc');
|
||||
mastGrd.addColorStop(1, _darken3D('#888', 0.42));
|
||||
ctx.fillStyle = mastGrd;
|
||||
ctx.fillRect(cx - pw/2, poleTop, pw, poleBot - poleTop);
|
||||
ctx.strokeStyle = '#2a2a2a'; ctx.lineWidth = 0.4;
|
||||
ctx.strokeRect(cx - pw/2, poleTop, pw, poleBot - poleTop);
|
||||
|
||||
// ── Topmark (3-D) ─────────────────────────────────────────────────────────
|
||||
if (isPort) {
|
||||
// Square CAN — 3-D cylinder (short, wide)
|
||||
const tg = ctx.createLinearGradient(cx - tmW/2, 0, cx + tmW/2, 0);
|
||||
tg.addColorStop(0, _lighten3D(col, 0.45));
|
||||
tg.addColorStop(0.42, col);
|
||||
tg.addColorStop(1, _darken3D(col, 0.45));
|
||||
ctx.fillStyle = tg;
|
||||
ctx.fillRect(cx - tmW/2, tmY - tmH, tmW, tmH);
|
||||
// Top highlight ellipse
|
||||
const ery = tmH * 0.13;
|
||||
const eg = ctx.createRadialGradient(cx - tmW*0.25, tmY - tmH, 1, cx, tmY - tmH, tmW/2);
|
||||
eg.addColorStop(0, 'rgba(255,255,255,0.70)'); eg.addColorStop(1, col);
|
||||
ctx.beginPath(); ctx.ellipse(cx, tmY - tmH, tmW/2, ery, 0, 0, Math.PI*2);
|
||||
ctx.fillStyle = eg; ctx.fill();
|
||||
// Bottom ellipse (shadow)
|
||||
ctx.beginPath(); ctx.ellipse(cx, tmY, tmW/2, ery, 0, 0, Math.PI*2);
|
||||
ctx.fillStyle = _darken3D(col, 0.45); ctx.fill();
|
||||
ctx.strokeStyle = '#222'; ctx.lineWidth = 0.6;
|
||||
ctx.strokeRect(cx - tmW/2, tmY - tmH, tmW, tmH);
|
||||
// ── Babor: rectángulo plano (tope cuadrado) ───────────────────────────
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bW / 2, bTop, bW, bBot - bTop);
|
||||
ctx.fillStyle = grd;
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// Highlight lateral izquierdo
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bW / 2, bTop, bW * 0.22, bBot - bTop);
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.16)';
|
||||
ctx.fill();
|
||||
} else {
|
||||
// CONE pointing up — 3-D gradient
|
||||
const apexY = tmY - tmH, baseY2 = tmY;
|
||||
const tg = ctx.createLinearGradient(cx - tmW/2, 0, cx + tmW/2, 0);
|
||||
tg.addColorStop(0, _lighten3D(col, 0.45));
|
||||
tg.addColorStop(0.42, col);
|
||||
tg.addColorStop(1, _darken3D(col, 0.45));
|
||||
// ── Estribor: triángulo (tope apuntado) + rectángulo (cuerpo) ──────────
|
||||
// Triángulo apuntando hacia arriba
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx, apexY); ctx.lineTo(cx + tmW/2, baseY2); ctx.lineTo(cx - tmW/2, baseY2);
|
||||
ctx.closePath(); ctx.fillStyle = tg; ctx.fill();
|
||||
// Base ellipse
|
||||
const ery2 = tmH * 0.10;
|
||||
ctx.beginPath(); ctx.ellipse(cx, baseY2, tmW/2, ery2, 0, 0, Math.PI*2);
|
||||
ctx.fillStyle = _darken3D(col, 0.40); ctx.fill();
|
||||
// Highlight streak
|
||||
ctx.moveTo(cx, bTop); // vértice superior
|
||||
ctx.lineTo(cx + bW / 2, bTop + triH); // esquina derecha de la base del tri
|
||||
ctx.lineTo(cx - bW / 2, bTop + triH); // esquina izquierda de la base del tri
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = grd;
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// Cuerpo rectangular debajo del triángulo
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx - tmW*0.05, apexY + tmH*0.08);
|
||||
ctx.lineTo(cx - tmW*0.28, baseY2 - ery2);
|
||||
ctx.strokeStyle = 'rgba(255,255,255,0.30)'; ctx.lineWidth = tmW * 0.07; ctx.stroke();
|
||||
ctx.strokeStyle = '#222'; ctx.lineWidth = 0.6;
|
||||
ctx.beginPath(); ctx.moveTo(cx, apexY); ctx.lineTo(cx+tmW/2, baseY2); ctx.lineTo(cx-tmW/2, baseY2); ctx.closePath(); ctx.stroke();
|
||||
ctx.rect(cx - bW / 2, bTop + triH, bW, bBot - (bTop + triH));
|
||||
ctx.fillStyle = grd;
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
// Highlight lateral izquierdo
|
||||
ctx.beginPath();
|
||||
ctx.rect(cx - bW / 2, bTop + triH, bW * 0.22, bBot - (bTop + triH));
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.16)';
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
return c;
|
||||
|
||||
Reference in New Issue
Block a user