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;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Light symbol — slim vertical post with a teardrop flame on top.
|
// ── White-light structure symbol (LIGHTS blanco / faro de orilla) ─────────────
|
||||||
// Colour follows the emitted light: white, red, green, yellow.
|
// 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) {
|
function _ialaLight(colourCode) {
|
||||||
const W = 14, H = 34;
|
const W = 26, H = 46;
|
||||||
const c = document.createElement('canvas');
|
const c = document.createElement('canvas');
|
||||||
c.width = W; c.height = H;
|
c.width = W; c.height = H;
|
||||||
const ctx = c.getContext('2d');
|
const ctx = c.getContext('2d');
|
||||||
|
const cx = W / 2;
|
||||||
|
|
||||||
|
// Sólo se invoca para luces blancas (kind==='flare'); defensivamente mapeamos colores.
|
||||||
let col = '#ffffff';
|
let col = '#ffffff';
|
||||||
if (colourCode === 3) col = '#dd0000';
|
if (colourCode === 3) col = '#dd1111';
|
||||||
else if (colourCode === 4) col = '#00aa00';
|
else if (colourCode === 4) col = '#00aa00';
|
||||||
else if (colourCode === 6) col = '#ffcc00';
|
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.beginPath();
|
||||||
ctx.moveTo(cx, 1);
|
ctx.rect(cx - bW / 2, bTop, bW, bH);
|
||||||
ctx.bezierCurveTo(cx + 4.5, 3.5, cx + 4.0, 9, cx, 11.5);
|
ctx.fillStyle = col; // blanco (o color para casos defensivos)
|
||||||
ctx.bezierCurveTo(cx - 4.0, 9, cx - 4.5, 3.5, cx, 1);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fillStyle = col;
|
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.lineWidth = 0.8;
|
|
||||||
ctx.strokeStyle = '#111';
|
ctx.strokeStyle = '#111';
|
||||||
|
ctx.lineWidth = 1.2;
|
||||||
ctx.stroke();
|
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.beginPath(); ctx.arc(cx, H - 2, 2.2, 0, Math.PI * 2);
|
||||||
ctx.fillStyle = '#fff'; ctx.fill();
|
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();
|
ctx.fillStyle = '#111'; ctx.fill();
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
@@ -1107,109 +1146,75 @@ function _encLightCanvas(colours, region, sz = 32) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Lateral beacon (BCNLAT) — 3-D tripod + pole + topmark ──────────────────
|
// ── Lateral beacon (BCNLAT) — rectángulo delgado alto + tope cuadrado/triangular ──
|
||||||
// Farillos de orilla: estructura fija (no flota). Tríangulo de patas + mástil
|
// Símbolo: cuerpo principal es un rectángulo delgado y alto en el color IALA lateral.
|
||||||
// vertical + marca de tope cuadrada (babor) o cónica (estribor).
|
// Babor (port, IALA-B verde): tope plano → rectángulo puro.
|
||||||
function _encBeaconCanvas(colours, catlam, region, sz = 44) {
|
// 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;
|
const c = _mkC(sz); const ctx = c.getContext('2d'); const cx = sz / 2;
|
||||||
let cc = (colours || []).slice();
|
let cc = (colours || []).slice();
|
||||||
if (!cc.length && catlam) cc = _ialaLateralColours(catlam, region);
|
if (!cc.length && catlam) cc = _ialaLateralColours(catlam, region);
|
||||||
const col = cc[0] ? _s57css(cc[0]) : '#78909c';
|
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
|
// ── Dimensiones del cuerpo ────────────────────────────────────────────────
|
||||||
const groundY = sz * 0.92; // ground level (shadow base)
|
const bW = sz * 0.30; // ancho del cuerpo (delgado)
|
||||||
const legHub = sz * 0.72; // where tripod legs converge
|
const bTop = sz * 0.07; // tope del cuerpo
|
||||||
const poleBot = legHub;
|
const bBot = sz * 0.90; // base (nivel del suelo)
|
||||||
const poleTop = sz * 0.32; // top of mast
|
const triH = bW * 1.05; // altura del triángulo = ≈ ancho (proporcional)
|
||||||
const tmH = sz * 0.18; // topmark height
|
const bodyTop = isPort ? bTop : bTop + triH; // tope del rectángulo (debajo del tri)
|
||||||
const tmW = sz * 0.46; // topmark width
|
|
||||||
const tmY = poleTop; // topmark sits at poleTop
|
|
||||||
|
|
||||||
// ── Subtle glow ──────────────────────────────────────────────────────────
|
// ── Sombra en la base ─────────────────────────────────────────────────────
|
||||||
const glow = ctx.createRadialGradient(cx, sz * 0.60, 0, cx, sz * 0.60, sz * 0.44);
|
ctx.beginPath();
|
||||||
glow.addColorStop(0, 'rgba(255,255,255,0.0)');
|
ctx.ellipse(cx, bBot, sz * 0.12, sz * 0.022, 0, 0, Math.PI * 2);
|
||||||
glow.addColorStop(0.65, 'rgba(180,210,255,0.10)');
|
ctx.fillStyle = 'rgba(0,0,0,0.22)';
|
||||||
glow.addColorStop(1, 'rgba(100,160,255,0.20)');
|
ctx.fill();
|
||||||
ctx.fillStyle = glow; ctx.fillRect(0, 0, sz, sz);
|
|
||||||
|
|
||||||
// ── Ground shadow ─────────────────────────────────────────────────────────
|
// ── Gradiente lateral (efecto 3-D suave) ─────────────────────────────────
|
||||||
ctx.beginPath(); ctx.ellipse(cx, groundY, sz * 0.22, sz * 0.04, 0, 0, Math.PI * 2);
|
const grd = ctx.createLinearGradient(cx - bW / 2, 0, cx + bW / 2, 0);
|
||||||
ctx.fillStyle = 'rgba(0,0,0,0.22)'; ctx.fill();
|
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) ───────────────────────────────
|
ctx.strokeStyle = 'rgba(0,0,0,0.65)';
|
||||||
// Back leg (darker, stub)
|
ctx.lineWidth = 0.9;
|
||||||
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();
|
|
||||||
|
|
||||||
// ── 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) {
|
if (isPort) {
|
||||||
// Square CAN — 3-D cylinder (short, wide)
|
// ── Babor: rectángulo plano (tope cuadrado) ───────────────────────────
|
||||||
const tg = ctx.createLinearGradient(cx - tmW/2, 0, cx + tmW/2, 0);
|
ctx.beginPath();
|
||||||
tg.addColorStop(0, _lighten3D(col, 0.45));
|
ctx.rect(cx - bW / 2, bTop, bW, bBot - bTop);
|
||||||
tg.addColorStop(0.42, col);
|
ctx.fillStyle = grd;
|
||||||
tg.addColorStop(1, _darken3D(col, 0.45));
|
ctx.fill();
|
||||||
ctx.fillStyle = tg;
|
ctx.stroke();
|
||||||
ctx.fillRect(cx - tmW/2, tmY - tmH, tmW, tmH);
|
// Highlight lateral izquierdo
|
||||||
// Top highlight ellipse
|
ctx.beginPath();
|
||||||
const ery = tmH * 0.13;
|
ctx.rect(cx - bW / 2, bTop, bW * 0.22, bBot - bTop);
|
||||||
const eg = ctx.createRadialGradient(cx - tmW*0.25, tmY - tmH, 1, cx, tmY - tmH, tmW/2);
|
ctx.fillStyle = 'rgba(255,255,255,0.16)';
|
||||||
eg.addColorStop(0, 'rgba(255,255,255,0.70)'); eg.addColorStop(1, col);
|
ctx.fill();
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
// CONE pointing up — 3-D gradient
|
// ── Estribor: triángulo (tope apuntado) + rectángulo (cuerpo) ──────────
|
||||||
const apexY = tmY - tmH, baseY2 = tmY;
|
// Triángulo apuntando hacia arriba
|
||||||
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.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(cx, apexY); ctx.lineTo(cx + tmW/2, baseY2); ctx.lineTo(cx - tmW/2, baseY2);
|
ctx.moveTo(cx, bTop); // vértice superior
|
||||||
ctx.closePath(); ctx.fillStyle = tg; ctx.fill();
|
ctx.lineTo(cx + bW / 2, bTop + triH); // esquina derecha de la base del tri
|
||||||
// Base ellipse
|
ctx.lineTo(cx - bW / 2, bTop + triH); // esquina izquierda de la base del tri
|
||||||
const ery2 = tmH * 0.10;
|
ctx.closePath();
|
||||||
ctx.beginPath(); ctx.ellipse(cx, baseY2, tmW/2, ery2, 0, 0, Math.PI*2);
|
ctx.fillStyle = grd;
|
||||||
ctx.fillStyle = _darken3D(col, 0.40); ctx.fill();
|
ctx.fill();
|
||||||
// Highlight streak
|
ctx.stroke();
|
||||||
|
// Cuerpo rectangular debajo del triángulo
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(cx - tmW*0.05, apexY + tmH*0.08);
|
ctx.rect(cx - bW / 2, bTop + triH, bW, bBot - (bTop + triH));
|
||||||
ctx.lineTo(cx - tmW*0.28, baseY2 - ery2);
|
ctx.fillStyle = grd;
|
||||||
ctx.strokeStyle = 'rgba(255,255,255,0.30)'; ctx.lineWidth = tmW * 0.07; ctx.stroke();
|
ctx.fill();
|
||||||
ctx.strokeStyle = '#222'; ctx.lineWidth = 0.6;
|
ctx.stroke();
|
||||||
ctx.beginPath(); ctx.moveTo(cx, apexY); ctx.lineTo(cx+tmW/2, baseY2); ctx.lineTo(cx-tmW/2, baseY2); ctx.closePath(); 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;
|
return c;
|
||||||
|
|||||||
Reference in New Issue
Block a user