Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b5a6b1b68d |
@@ -22,11 +22,14 @@ type MarkersApiResponse = {
|
|||||||
|
|
||||||
// ── Coordinate transform ──────────────────────────────────────────────────────
|
// ── Coordinate transform ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
// Iran bounding box (degrees)
|
// Iran bounding box (degrees) — fitted to the real border extent
|
||||||
const MIN_LNG = 44;
|
// (lng 44.11–63.32, lat 25.08–39.71) with a small margin so the
|
||||||
const MAX_LNG = 64;
|
// silhouette fills the viewBox. Markers reproject with the same box,
|
||||||
const MIN_LAT = 24;
|
// so they stay aligned with the outline.
|
||||||
const MAX_LAT = 41;
|
const MIN_LNG = 43.6;
|
||||||
|
const MAX_LNG = 63.8;
|
||||||
|
const MIN_LAT = 24.6;
|
||||||
|
const MAX_LAT = 40.2;
|
||||||
const SVG_W = 600;
|
const SVG_W = 600;
|
||||||
const SVG_H = 500;
|
const SVG_H = 500;
|
||||||
|
|
||||||
@@ -41,34 +44,24 @@ function toPt([lng, lat]: [number, number]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Iran silhouette ────────────────────────────────────────────────────────────
|
// ── Iran silhouette ────────────────────────────────────────────────────────────
|
||||||
// Simplified 40-point polygon; approximate but recognisable.
|
// Real national border, simplified to 74 vertices (source: Natural Earth via
|
||||||
// Coordinates are [longitude, latitude] going clockwise from NW.
|
// world.geo.json). Coordinates are [longitude, latitude]; the ring starts on
|
||||||
|
// the Caspian (NE) and runs clockwise. Projected through toX/toY below, the
|
||||||
|
// same transform used for the café markers, so dots land in the right place.
|
||||||
const IRAN_OUTLINE: [number, number][] = [
|
const IRAN_OUTLINE: [number, number][] = [
|
||||||
// NW corner / Turkey-Armenia-Azerbaijan
|
[53.92, 37.20], [54.80, 37.39], [55.51, 37.96], [56.18, 37.94], [56.62, 38.12], [57.33, 38.03],
|
||||||
[44.8, 39.6], [45.5, 39.2], [46.2, 38.9],
|
[58.44, 37.52], [59.23, 37.41], [60.38, 36.53], [61.12, 36.49], [61.21, 35.65], [60.80, 34.40],
|
||||||
[46.8, 39.1], [47.6, 38.9],
|
[60.53, 33.68], [60.96, 33.53], [60.54, 32.98], [60.86, 32.18], [60.94, 31.55], [61.70, 31.38],
|
||||||
// Caspian coast (the concave notch heading south then north again)
|
[61.78, 30.74], [60.87, 29.83], [61.37, 29.30], [61.77, 28.70], [62.73, 28.26], [62.76, 27.38],
|
||||||
[48.3, 38.4], [49.0, 37.5], [49.9, 37.2],
|
[63.23, 27.22], [63.32, 26.76], [61.87, 26.24], [61.50, 25.08], [59.62, 25.38], [58.53, 25.61],
|
||||||
[51.0, 36.9], [52.2, 36.8], [53.0, 36.7],
|
[57.40, 25.74], [56.97, 26.97], [56.49, 27.14], [55.72, 26.96], [54.72, 26.48], [53.49, 26.81],
|
||||||
[54.0, 37.1], [54.7, 37.5],
|
[52.48, 27.58], [51.52, 27.87], [50.85, 28.81], [50.12, 30.15], [49.58, 29.99], [48.94, 30.32],
|
||||||
// NE / Turkmenistan
|
[48.57, 29.93], [48.01, 30.45], [48.00, 30.99], [47.69, 30.98], [47.85, 31.71], [47.33, 32.47],
|
||||||
[55.6, 37.4], [56.9, 37.1], [57.7, 36.8],
|
[46.11, 33.02], [45.42, 33.97], [45.65, 34.75], [46.15, 35.09], [46.08, 35.68], [45.42, 35.98],
|
||||||
[58.7, 37.5], [59.4, 36.8], [60.1, 36.7],
|
[44.77, 37.17], [44.23, 37.97], [44.42, 38.28], [44.11, 39.43], [44.79, 39.71], [44.95, 39.34],
|
||||||
// East / Afghanistan
|
[45.46, 38.87], [46.14, 38.74], [46.51, 38.77], [47.69, 39.51], [48.06, 39.58], [48.36, 39.29],
|
||||||
[61.2, 36.5], [61.3, 35.7], [62.0, 35.5],
|
[48.01, 38.79], [48.63, 38.27], [48.88, 38.32], [49.20, 37.58], [50.15, 37.37], [50.84, 36.87],
|
||||||
[62.5, 34.0], [63.0, 33.0], [63.2, 31.5],
|
[52.26, 36.70], [53.83, 36.97],
|
||||||
// SE / Pakistan – Oman Sea
|
|
||||||
[61.8, 29.8], [60.9, 29.5], [60.0, 27.5],
|
|
||||||
[59.0, 25.9], [58.5, 25.4],
|
|
||||||
// South coast (Persian Gulf, west-bound)
|
|
||||||
[57.5, 25.3], [56.4, 25.9], [55.6, 26.0],
|
|
||||||
[54.5, 27.0], [53.4, 27.3], [52.4, 28.0],
|
|
||||||
[51.1, 28.4], [50.4, 29.1], [49.0, 29.6],
|
|
||||||
[48.5, 30.2], [48.2, 30.8],
|
|
||||||
// West / Iraq border
|
|
||||||
[47.7, 31.0], [47.2, 32.0], [46.8, 33.2],
|
|
||||||
[46.2, 34.4], [45.5, 36.0], [45.0, 37.0],
|
|
||||||
[44.8, 38.1], [44.5, 38.9], [44.8, 39.6],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const IRAN_PATH =
|
const IRAN_PATH =
|
||||||
@@ -153,42 +146,50 @@ async function IranMapSvg() {
|
|||||||
</g>
|
</g>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Café blinking dots */}
|
{/* Café markers — each glows slowly on and off like a small lamp.
|
||||||
|
Halo and core brighten/dim together (ease-in-out), staggered so the
|
||||||
|
map twinkles organically rather than pulsing in unison. */}
|
||||||
{markers.map((m, idx) => {
|
{markers.map((m, idx) => {
|
||||||
const cx = toX(m.longitude);
|
const cx = toX(m.longitude);
|
||||||
const cy = toY(m.latitude);
|
const cy = toY(m.latitude);
|
||||||
// Stagger animation delay so dots don't all pulse in sync
|
const delay = `${((idx * 0.7) % 3.6).toFixed(2)}s`;
|
||||||
const delay = `${(idx * 0.4) % 2}s`;
|
const dur = "3.6s";
|
||||||
|
// ease-in-out for a smooth lamp-like fade
|
||||||
|
const ease = "0.4 0 0.6 1; 0.4 0 0.6 1";
|
||||||
return (
|
return (
|
||||||
<g key={m.id} filter="url(#glow)">
|
<g key={m.id} filter="url(#glow)">
|
||||||
{/* Outer pulse ring */}
|
{/* Soft halo */}
|
||||||
<circle cx={cx} cy={cy} r={10} fill="#0F6E56" opacity={0.2}>
|
<circle cx={cx} cy={cy} r={9} fill="#0F6E56">
|
||||||
<animate
|
|
||||||
attributeName="r"
|
|
||||||
values="8;16;8"
|
|
||||||
dur="2.4s"
|
|
||||||
begin={delay}
|
|
||||||
repeatCount="indefinite"
|
|
||||||
/>
|
|
||||||
<animate
|
<animate
|
||||||
attributeName="opacity"
|
attributeName="opacity"
|
||||||
values="0.25;0;0.25"
|
values="0.45;0.04;0.45"
|
||||||
dur="2.4s"
|
keyTimes="0;0.5;1"
|
||||||
|
calcMode="spline"
|
||||||
|
keySplines={ease}
|
||||||
|
dur={dur}
|
||||||
begin={delay}
|
begin={delay}
|
||||||
repeatCount="indefinite"
|
repeatCount="indefinite"
|
||||||
/>
|
/>
|
||||||
</circle>
|
</circle>
|
||||||
{/* Core dot */}
|
{/* Core dot — turns on (bright, slightly larger) and off (dim) */}
|
||||||
<circle
|
<circle cx={cx} cy={cy} r={4.5} fill="#0F6E56">
|
||||||
cx={cx}
|
|
||||||
cy={cy}
|
|
||||||
r={5}
|
|
||||||
fill="#0F6E56"
|
|
||||||
>
|
|
||||||
<animate
|
<animate
|
||||||
attributeName="opacity"
|
attributeName="opacity"
|
||||||
values="1;0.5;1"
|
values="1;0.2;1"
|
||||||
dur="2.4s"
|
keyTimes="0;0.5;1"
|
||||||
|
calcMode="spline"
|
||||||
|
keySplines={ease}
|
||||||
|
dur={dur}
|
||||||
|
begin={delay}
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
<animate
|
||||||
|
attributeName="r"
|
||||||
|
values="4.5;5.6;4.5"
|
||||||
|
keyTimes="0;0.5;1"
|
||||||
|
calcMode="spline"
|
||||||
|
keySplines={ease}
|
||||||
|
dur={dur}
|
||||||
begin={delay}
|
begin={delay}
|
||||||
repeatCount="indefinite"
|
repeatCount="indefinite"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user