diff --git a/dashboard/public/jarvis.html b/dashboard/public/jarvis.html index 0be78650..d3a368c5 100644 --- a/dashboard/public/jarvis.html +++ b/dashboard/public/jarvis.html @@ -67,14 +67,15 @@ /* GRID */ .grid{display:grid;grid-template-columns:240px 1fr 340px;gap:10px;margin-top:10px;min-height:calc(100vh - 100px)} .col{display:flex;flex-direction:column;gap:10px} -.g-panel{border:1px solid var(--border);background:var(--glass);backdrop-filter:blur(16px);padding:12px;position:relative;overflow:hidden} +#leftRail{overflow-y:auto} +.g-panel{border:1px solid var(--border);background:var(--glass);backdrop-filter:blur(16px);padding:12px;position:relative;flex-shrink:0} .g-panel::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,rgba(100,240,200,0.15),transparent);pointer-events:none} .sec-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px} .sec-head h3{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:0.14em;text-transform:uppercase;color:var(--dim)} .badge{font-family:var(--mono);font-size:10px;padding:2px 7px;border:1px solid var(--border);color:var(--accent)} /* LEFT RAIL */ -.layer-item{display:flex;align-items:center;justify-content:space-between;padding:8px;border:1px solid rgba(255,255,255,0.04);background:rgba(255,255,255,0.02);margin-bottom:4px} +.layer-item{display:flex;align-items:center;justify-content:space-between;padding:8px;border:1px solid rgba(255,255,255,0.04);background:rgba(255,255,255,0.02);margin-bottom:4px;cursor:pointer;transition:opacity 0.2s,border-color 0.2s} .layer-left{display:flex;align-items:center;gap:8px} .ldot{width:10px;height:10px;border-radius:50%;flex-shrink:0} .ldot.air{background:var(--accent);box-shadow:0 0 6px rgba(100,240,200,0.4)} @@ -86,6 +87,18 @@ .ldot.health{background:#69f0ae;box-shadow:0 0 6px rgba(105,240,174,0.4)} .ldot.news{background:#81d4fa;box-shadow:0 0 6px rgba(129,212,250,0.4)} .ldot.space{background:#e0b0ff;box-shadow:0 0 6px rgba(224,176,255,0.4)} +.ldot.osint{background:var(--warn);box-shadow:0 0 6px rgba(255,184,76,0.4)} +.ldot.noaa{background:#ff9800;box-shadow:0 0 6px rgba(255,152,0,0.4)} +.ldot.epa{background:#cddc39;box-shadow:0 0 6px rgba(205,220,57,0.4)} +.ldot.gdelt{background:#6495ed;box-shadow:0 0 6px rgba(100,149,237,0.4)} +.ldot.capital{background:#ffd54f;box-shadow:0 0 6px rgba(255,213,79,0.4)} +.layer-item-off{opacity:0.35} +.layer-item-off .ldot{box-shadow:none} +.layer-item-off .layer-name{color:#8899aa} +.layer-item-off .layer-sub{color:#445566} +.layer-item-off .layer-count{color:#556677} +.layer-eye{font-size:11px;margin-left:6px;flex-shrink:0;opacity:0.5} +.layer-item-off .layer-eye{opacity:0.2} .layer-name{font-size:12px;font-weight:500} .layer-sub{font-size:10px;color:var(--dim)} .layer-count{font-family:var(--mono);font-size:13px;font-weight:600;color:var(--accent)} @@ -346,7 +359,6 @@
-
@@ -392,9 +404,291 @@ // === GLOBALS === let globe = null; let globeInitialized = false; -let flightsVisible = true; -let lowPerfMode = localStorage.getItem('crucix_low_perf') === 'true'; +let lowPerfMode = false; +try { lowPerfMode = localStorage.getItem('crucix_low_perf') === 'true'; } catch (e) { console.warn('[Crucix] localStorage unavailable:', e.message); } let isFlat = shouldStartFlat(); + +// === LAYER TOGGLE STATE === +const LAYERS = [ + {id:'air', label:'Air Activity', color:'#64f0c8', dot:'air'}, + {id:'thermal', label:'Thermal Spikes', color:'#ff5f63', dot:'thermal'}, + {id:'sdr', label:'SDR Coverage', color:'#44ccff', dot:'sdr'}, + {id:'maritime',label:'Maritime Watch', color:'#b388ff', dot:'maritime'}, + {id:'nuke', label:'Nuclear Sites', color:'#ffe082', dot:'nuke'}, + {id:'acled', label:'Conflict Events',color:'rgba(255,120,80,0.8)',dot:'incident'}, + {id:'who', label:'Health Watch', color:'#69f0ae', dot:'health'}, + {id:'news', label:'World News', color:'#81d4fa', dot:'news'}, + {id:'osint', label:'OSINT Feed', color:'#ffb84c', dot:'osint'}, + {id:'space', label:'Satellites', color:'#e0b0ff', dot:'space'}, + {id:'noaa', label:'Weather Alerts', color:'#ff9800', dot:'noaa'}, + {id:'epa', label:'EPA RadNet', color:'#cddc39', dot:'epa'}, + {id:'gdelt', label:'GDELT Events', color:'#6495ed', dot:'gdelt'}, + {id:'capitals',label:'World Capitals',color:'#ffd54f', dot:'capital'}, +]; +let layerVisibility = Object.fromEntries(LAYERS.map(l => [l.id, true])); +try { + const saved = JSON.parse(localStorage.getItem('crucix-layers')); + if (saved && typeof saved === 'object') { + for (const l of LAYERS) if (saved[l.id] !== undefined) layerVisibility[l.id] = saved[l.id]; + } +} catch (e) { console.warn('[Crucix] Could not restore layer preferences:', e.message); } +function toggleLayer(id) { + if (!LAYERS.some(l => l.id === id)) return; + layerVisibility[id] = !layerVisibility[id]; + try { localStorage.setItem('crucix-layers', JSON.stringify(layerVisibility)); } + catch (e) { console.warn('[Crucix] Could not save layer preferences:', e.message); } + renderLeftRail(); + renderMapLegend(); + if (!isFlat) plotMarkers(); + if (isFlat && flatG) { flatG.selectAll('*').remove(); drawFlatMap(); } +} +const CAPITALS=[ +{name:"Kabul",country:"Afghanistan",iso2:"AF",flag:"\ud83c\udde6\ud83c\uddeb",lat:34.52,lng:69.18}, +{name:"Tirana",country:"Albania",iso2:"AL",flag:"\ud83c\udde6\ud83c\uddf1",lat:41.32,lng:19.82}, +{name:"Algiers",country:"Algeria",iso2:"DZ",flag:"\ud83c\udde9\ud83c\uddff",lat:36.75,lng:3.05}, +{name:"Pago Pago",country:"American Samoa",iso2:"AS",flag:"\ud83c\udde6\ud83c\uddf8",lat:-14.27,lng:-170.7}, +{name:"Andorra la Vella",country:"Andorra",iso2:"AD",flag:"\ud83c\udde6\ud83c\udde9",lat:42.5,lng:1.52}, +{name:"Luanda",country:"Angola",iso2:"AO",flag:"\ud83c\udde6\ud83c\uddf4",lat:-8.83,lng:13.22}, +{name:"The Valley",country:"Anguilla",iso2:"AI",flag:"\ud83c\udde6\ud83c\uddee",lat:18.22,lng:-63.05}, +{name:"Saint John's",country:"Antigua and Barbuda",iso2:"AG",flag:"\ud83c\udde6\ud83c\uddec",lat:17.12,lng:-61.85}, +{name:"Buenos Aires",country:"Argentina",iso2:"AR",flag:"\ud83c\udde6\ud83c\uddf7",lat:-34.58,lng:-58.67}, +{name:"Yerevan",country:"Armenia",iso2:"AM",flag:"\ud83c\udde6\ud83c\uddf2",lat:40.17,lng:44.5}, +{name:"Oranjestad",country:"Aruba",iso2:"AW",flag:"\ud83c\udde6\ud83c\uddfc",lat:12.52,lng:-70.03}, +{name:"Canberra",country:"Australia",iso2:"AU",flag:"\ud83c\udde6\ud83c\uddfa",lat:-35.27,lng:149.13}, +{name:"Vienna",country:"Austria",iso2:"AT",flag:"\ud83c\udde6\ud83c\uddf9",lat:48.2,lng:16.37}, +{name:"Baku",country:"Azerbaijan",iso2:"AZ",flag:"\ud83c\udde6\ud83c\uddff",lat:40.38,lng:49.87}, +{name:"Nassau",country:"Bahamas",iso2:"BS",flag:"\ud83c\udde7\ud83c\uddf8",lat:25.08,lng:-77.35}, +{name:"Manama",country:"Bahrain",iso2:"BH",flag:"\ud83c\udde7\ud83c\udded",lat:26.23,lng:50.57}, +{name:"Dhaka",country:"Bangladesh",iso2:"BD",flag:"\ud83c\udde7\ud83c\udde9",lat:23.72,lng:90.4}, +{name:"Bridgetown",country:"Barbados",iso2:"BB",flag:"\ud83c\udde7\ud83c\udde7",lat:13.1,lng:-59.62}, +{name:"Minsk",country:"Belarus",iso2:"BY",flag:"\ud83c\udde7\ud83c\uddfe",lat:53.9,lng:27.57}, +{name:"Brussels",country:"Belgium",iso2:"BE",flag:"\ud83c\udde7\ud83c\uddea",lat:50.83,lng:4.33}, +{name:"Belmopan",country:"Belize",iso2:"BZ",flag:"\ud83c\udde7\ud83c\uddff",lat:17.25,lng:-88.77}, +{name:"Porto-Novo",country:"Benin",iso2:"BJ",flag:"\ud83c\udde7\ud83c\uddef",lat:6.48,lng:2.62}, +{name:"Hamilton",country:"Bermuda",iso2:"BM",flag:"\ud83c\udde7\ud83c\uddf2",lat:32.28,lng:-64.78}, +{name:"Thimphu",country:"Bhutan",iso2:"BT",flag:"\ud83c\udde7\ud83c\uddf9",lat:27.47,lng:89.63}, +{name:"Sucre",country:"Bolivia",iso2:"BO",flag:"\ud83c\udde7\ud83c\uddf4",lat:-19.02,lng:-65.26}, +{name:"Sarajevo",country:"Bosnia and Herzegovina",iso2:"BA",flag:"\ud83c\udde7\ud83c\udde6",lat:43.87,lng:18.42}, +{name:"Gaborone",country:"Botswana",iso2:"BW",flag:"\ud83c\udde7\ud83c\uddfc",lat:-24.63,lng:25.9}, +{name:"Bras\u00edlia",country:"Brazil",iso2:"BR",flag:"\ud83c\udde7\ud83c\uddf7",lat:-15.79,lng:-47.88}, +{name:"Diego Garcia",country:"British Indian Ocean Territory",iso2:"IO",flag:"\ud83c\uddee\ud83c\uddf4",lat:-7.3,lng:72.4}, +{name:"Road Town",country:"British Virgin Islands",iso2:"VG",flag:"\ud83c\uddfb\ud83c\uddec",lat:18.42,lng:-64.62}, +{name:"Bandar Seri Begawan",country:"Brunei",iso2:"BN",flag:"\ud83c\udde7\ud83c\uddf3",lat:4.88,lng:114.93}, +{name:"Sofia",country:"Bulgaria",iso2:"BG",flag:"\ud83c\udde7\ud83c\uddec",lat:42.68,lng:23.32}, +{name:"Ouagadougou",country:"Burkina Faso",iso2:"BF",flag:"\ud83c\udde7\ud83c\uddeb",lat:12.37,lng:-1.52}, +{name:"Gitega",country:"Burundi",iso2:"BI",flag:"\ud83c\udde7\ud83c\uddee",lat:-3.43,lng:29.93}, +{name:"Phnom Penh",country:"Cambodia",iso2:"KH",flag:"\ud83c\uddf0\ud83c\udded",lat:11.55,lng:104.92}, +{name:"Yaound\u00e9",country:"Cameroon",iso2:"CM",flag:"\ud83c\udde8\ud83c\uddf2",lat:3.85,lng:11.5}, +{name:"Ottawa",country:"Canada",iso2:"CA",flag:"\ud83c\udde8\ud83c\udde6",lat:45.42,lng:-75.7}, +{name:"Praia",country:"Cape Verde",iso2:"CV",flag:"\ud83c\udde8\ud83c\uddfb",lat:14.92,lng:-23.52}, +{name:"Kralendijk",country:"Caribbean Netherlands",iso2:"BQ",flag:"\ud83c\udde7\ud83c\uddf6",lat:12.14,lng:-68.27}, +{name:"George Town",country:"Cayman Islands",iso2:"KY",flag:"\ud83c\uddf0\ud83c\uddfe",lat:19.3,lng:-81.38}, +{name:"Bangui",country:"Central African Republic",iso2:"CF",flag:"\ud83c\udde8\ud83c\uddeb",lat:4.37,lng:18.58}, +{name:"N'Djamena",country:"Chad",iso2:"TD",flag:"\ud83c\uddf9\ud83c\udde9",lat:12.1,lng:15.03}, +{name:"Santiago",country:"Chile",iso2:"CL",flag:"\ud83c\udde8\ud83c\uddf1",lat:-33.45,lng:-70.67}, +{name:"Beijing",country:"China",iso2:"CN",flag:"\ud83c\udde8\ud83c\uddf3",lat:39.92,lng:116.38}, +{name:"Flying Fish Cove",country:"Christmas Island",iso2:"CX",flag:"\ud83c\udde8\ud83c\uddfd",lat:-10.42,lng:105.68}, +{name:"West Island",country:"Cocos (Keeling) Islands",iso2:"CC",flag:"\ud83c\udde8\ud83c\udde8",lat:-12.17,lng:96.83}, +{name:"Bogot\u00e1",country:"Colombia",iso2:"CO",flag:"\ud83c\udde8\ud83c\uddf4",lat:4.71,lng:-74.07}, +{name:"Moroni",country:"Comoros",iso2:"KM",flag:"\ud83c\uddf0\ud83c\uddf2",lat:-11.7,lng:43.23}, +{name:"Avarua",country:"Cook Islands",iso2:"CK",flag:"\ud83c\udde8\ud83c\uddf0",lat:-21.2,lng:-159.77}, +{name:"San Jos\u00e9",country:"Costa Rica",iso2:"CR",flag:"\ud83c\udde8\ud83c\uddf7",lat:9.93,lng:-84.09}, +{name:"Zagreb",country:"Croatia",iso2:"HR",flag:"\ud83c\udded\ud83c\uddf7",lat:45.8,lng:16}, +{name:"Havana",country:"Cuba",iso2:"CU",flag:"\ud83c\udde8\ud83c\uddfa",lat:23.12,lng:-82.35}, +{name:"Willemstad",country:"Cura\u00e7ao",iso2:"CW",flag:"\ud83c\udde8\ud83c\uddfc",lat:12.1,lng:-68.92}, +{name:"Nicosia",country:"Cyprus",iso2:"CY",flag:"\ud83c\udde8\ud83c\uddfe",lat:35.17,lng:33.37}, +{name:"Prague",country:"Czechia",iso2:"CZ",flag:"\ud83c\udde8\ud83c\uddff",lat:50.08,lng:14.47}, +{name:"Kinshasa",country:"DR Congo",iso2:"CD",flag:"\ud83c\udde8\ud83c\udde9",lat:-4.32,lng:15.3}, +{name:"Copenhagen",country:"Denmark",iso2:"DK",flag:"\ud83c\udde9\ud83c\uddf0",lat:55.67,lng:12.58}, +{name:"Djibouti",country:"Djibouti",iso2:"DJ",flag:"\ud83c\udde9\ud83c\uddef",lat:11.58,lng:43.15}, +{name:"Roseau",country:"Dominica",iso2:"DM",flag:"\ud83c\udde9\ud83c\uddf2",lat:15.3,lng:-61.4}, +{name:"Santo Domingo",country:"Dominican Republic",iso2:"DO",flag:"\ud83c\udde9\ud83c\uddf4",lat:18.47,lng:-69.9}, +{name:"Quito",country:"Ecuador",iso2:"EC",flag:"\ud83c\uddea\ud83c\udde8",lat:-0.22,lng:-78.5}, +{name:"Cairo",country:"Egypt",iso2:"EG",flag:"\ud83c\uddea\ud83c\uddec",lat:30.05,lng:31.25}, +{name:"San Salvador",country:"El Salvador",iso2:"SV",flag:"\ud83c\uddf8\ud83c\uddfb",lat:13.7,lng:-89.2}, +{name:"Ciudad de la Paz",country:"Equatorial Guinea",iso2:"GQ",flag:"\ud83c\uddec\ud83c\uddf6",lat:1.35,lng:10.49}, +{name:"Asmara",country:"Eritrea",iso2:"ER",flag:"\ud83c\uddea\ud83c\uddf7",lat:15.33,lng:38.93}, +{name:"Tallinn",country:"Estonia",iso2:"EE",flag:"\ud83c\uddea\ud83c\uddea",lat:59.43,lng:24.72}, +{name:"Mbabane",country:"Eswatini",iso2:"SZ",flag:"\ud83c\uddf8\ud83c\uddff",lat:-26.32,lng:31.13}, +{name:"Addis Ababa",country:"Ethiopia",iso2:"ET",flag:"\ud83c\uddea\ud83c\uddf9",lat:9.03,lng:38.7}, +{name:"Stanley",country:"Falkland Islands",iso2:"FK",flag:"\ud83c\uddeb\ud83c\uddf0",lat:-51.7,lng:-57.85}, +{name:"T\u00f3rshavn",country:"Faroe Islands",iso2:"FO",flag:"\ud83c\uddeb\ud83c\uddf4",lat:62.01,lng:-6.77}, +{name:"Suva",country:"Fiji",iso2:"FJ",flag:"\ud83c\uddeb\ud83c\uddef",lat:-18.13,lng:178.42}, +{name:"Helsinki",country:"Finland",iso2:"FI",flag:"\ud83c\uddeb\ud83c\uddee",lat:60.17,lng:24.93}, +{name:"Paris",country:"France",iso2:"FR",flag:"\ud83c\uddeb\ud83c\uddf7",lat:48.87,lng:2.33}, +{name:"Cayenne",country:"French Guiana",iso2:"GF",flag:"\ud83c\uddec\ud83c\uddeb",lat:4.94,lng:-52.33}, +{name:"Papeet\u0113",country:"French Polynesia",iso2:"PF",flag:"\ud83c\uddf5\ud83c\uddeb",lat:-17.53,lng:-149.56}, +{name:"Port-aux-Fran\u00e7ais",country:"French Southern and Antarctic Lands",iso2:"TF",flag:"\ud83c\uddf9\ud83c\uddeb",lat:-49.35,lng:70.22}, +{name:"Libreville",country:"Gabon",iso2:"GA",flag:"\ud83c\uddec\ud83c\udde6",lat:0.38,lng:9.45}, +{name:"Banjul",country:"Gambia",iso2:"GM",flag:"\ud83c\uddec\ud83c\uddf2",lat:13.45,lng:-16.57}, +{name:"Tbilisi",country:"Georgia",iso2:"GE",flag:"\ud83c\uddec\ud83c\uddea",lat:41.68,lng:44.83}, +{name:"Berlin",country:"Germany",iso2:"DE",flag:"\ud83c\udde9\ud83c\uddea",lat:52.52,lng:13.4}, +{name:"Accra",country:"Ghana",iso2:"GH",flag:"\ud83c\uddec\ud83c\udded",lat:5.55,lng:-0.22}, +{name:"Gibraltar",country:"Gibraltar",iso2:"GI",flag:"\ud83c\uddec\ud83c\uddee",lat:36.13,lng:-5.35}, +{name:"Athens",country:"Greece",iso2:"GR",flag:"\ud83c\uddec\ud83c\uddf7",lat:37.98,lng:23.73}, +{name:"Nuuk",country:"Greenland",iso2:"GL",flag:"\ud83c\uddec\ud83c\uddf1",lat:64.18,lng:-51.75}, +{name:"St. George's",country:"Grenada",iso2:"GD",flag:"\ud83c\uddec\ud83c\udde9",lat:12.05,lng:-61.75}, +{name:"Basse-Terre",country:"Guadeloupe",iso2:"GP",flag:"\ud83c\uddec\ud83c\uddf5",lat:16.03,lng:-61.73}, +{name:"Hag\u00e5t\u00f1a",country:"Guam",iso2:"GU",flag:"\ud83c\uddec\ud83c\uddfa",lat:13.48,lng:144.75}, +{name:"Guatemala City",country:"Guatemala",iso2:"GT",flag:"\ud83c\uddec\ud83c\uddf9",lat:14.62,lng:-90.52}, +{name:"St. Peter Port",country:"Guernsey",iso2:"GG",flag:"\ud83c\uddec\ud83c\uddec",lat:49.45,lng:-2.54}, +{name:"Conakry",country:"Guinea",iso2:"GN",flag:"\ud83c\uddec\ud83c\uddf3",lat:9.5,lng:-13.7}, +{name:"Bissau",country:"Guinea-Bissau",iso2:"GW",flag:"\ud83c\uddec\ud83c\uddfc",lat:11.85,lng:-15.58}, +{name:"Georgetown",country:"Guyana",iso2:"GY",flag:"\ud83c\uddec\ud83c\uddfe",lat:6.8,lng:-58.15}, +{name:"Port-au-Prince",country:"Haiti",iso2:"HT",flag:"\ud83c\udded\ud83c\uddf9",lat:18.53,lng:-72.33}, +{name:"Tegucigalpa",country:"Honduras",iso2:"HN",flag:"\ud83c\udded\ud83c\uddf3",lat:14.1,lng:-87.22}, +{name:"City of Victoria",country:"Hong Kong",iso2:"HK",flag:"\ud83c\udded\ud83c\uddf0",lat:22.267,lng:114.188}, +{name:"Budapest",country:"Hungary",iso2:"HU",flag:"\ud83c\udded\ud83c\uddfa",lat:47.5,lng:19.08}, +{name:"Reykjavik",country:"Iceland",iso2:"IS",flag:"\ud83c\uddee\ud83c\uddf8",lat:64.15,lng:-21.95}, +{name:"New Delhi",country:"India",iso2:"IN",flag:"\ud83c\uddee\ud83c\uddf3",lat:28.6,lng:77.2}, +{name:"Jakarta",country:"Indonesia",iso2:"ID",flag:"\ud83c\uddee\ud83c\udde9",lat:-6.17,lng:106.82}, +{name:"Tehran",country:"Iran",iso2:"IR",flag:"\ud83c\uddee\ud83c\uddf7",lat:35.7,lng:51.42}, +{name:"Baghdad",country:"Iraq",iso2:"IQ",flag:"\ud83c\uddee\ud83c\uddf6",lat:33.33,lng:44.4}, +{name:"Dublin",country:"Ireland",iso2:"IE",flag:"\ud83c\uddee\ud83c\uddea",lat:53.32,lng:-6.23}, +{name:"Douglas",country:"Isle of Man",iso2:"IM",flag:"\ud83c\uddee\ud83c\uddf2",lat:54.15,lng:-4.48}, +{name:"Jerusalem",country:"Israel",iso2:"IL",flag:"\ud83c\uddee\ud83c\uddf1",lat:31.77,lng:35.23}, +{name:"Rome",country:"Italy",iso2:"IT",flag:"\ud83c\uddee\ud83c\uddf9",lat:41.9,lng:12.48}, +{name:"Yamoussoukro",country:"Ivory Coast",iso2:"CI",flag:"\ud83c\udde8\ud83c\uddee",lat:6.82,lng:-5.27}, +{name:"Kingston",country:"Jamaica",iso2:"JM",flag:"\ud83c\uddef\ud83c\uddf2",lat:17.997,lng:-76.7936}, +{name:"Tokyo",country:"Japan",iso2:"JP",flag:"\ud83c\uddef\ud83c\uddf5",lat:35.68,lng:139.75}, +{name:"Saint Helier",country:"Jersey",iso2:"JE",flag:"\ud83c\uddef\ud83c\uddea",lat:49.18,lng:-2.1}, +{name:"Amman",country:"Jordan",iso2:"JO",flag:"\ud83c\uddef\ud83c\uddf4",lat:31.95,lng:35.93}, +{name:"Astana",country:"Kazakhstan",iso2:"KZ",flag:"\ud83c\uddf0\ud83c\uddff",lat:51.16,lng:71.45}, +{name:"Nairobi",country:"Kenya",iso2:"KE",flag:"\ud83c\uddf0\ud83c\uddea",lat:-1.28,lng:36.82}, +{name:"South Tarawa",country:"Kiribati",iso2:"KI",flag:"\ud83c\uddf0\ud83c\uddee",lat:1.33,lng:172.98}, +{name:"Pristina",country:"Kosovo",iso2:"XK",flag:"\ud83c\uddfd\ud83c\uddf0",lat:42.67,lng:21.17}, +{name:"Kuwait City",country:"Kuwait",iso2:"KW",flag:"\ud83c\uddf0\ud83c\uddfc",lat:29.37,lng:47.97}, +{name:"Bishkek",country:"Kyrgyzstan",iso2:"KG",flag:"\ud83c\uddf0\ud83c\uddec",lat:42.87,lng:74.6}, +{name:"Vientiane",country:"Laos",iso2:"LA",flag:"\ud83c\uddf1\ud83c\udde6",lat:17.97,lng:102.6}, +{name:"Riga",country:"Latvia",iso2:"LV",flag:"\ud83c\uddf1\ud83c\uddfb",lat:56.95,lng:24.1}, +{name:"Beirut",country:"Lebanon",iso2:"LB",flag:"\ud83c\uddf1\ud83c\udde7",lat:33.87,lng:35.5}, +{name:"Maseru",country:"Lesotho",iso2:"LS",flag:"\ud83c\uddf1\ud83c\uddf8",lat:-29.32,lng:27.48}, +{name:"Monrovia",country:"Liberia",iso2:"LR",flag:"\ud83c\uddf1\ud83c\uddf7",lat:6.3,lng:-10.8}, +{name:"Tripoli",country:"Libya",iso2:"LY",flag:"\ud83c\uddf1\ud83c\uddfe",lat:32.88,lng:13.17}, +{name:"Vaduz",country:"Liechtenstein",iso2:"LI",flag:"\ud83c\uddf1\ud83c\uddee",lat:47.13,lng:9.52}, +{name:"Vilnius",country:"Lithuania",iso2:"LT",flag:"\ud83c\uddf1\ud83c\uddf9",lat:54.68,lng:25.32}, +{name:"Luxembourg",country:"Luxembourg",iso2:"LU",flag:"\ud83c\uddf1\ud83c\uddfa",lat:49.6,lng:6.12}, +{name:"Antananarivo",country:"Madagascar",iso2:"MG",flag:"\ud83c\uddf2\ud83c\uddec",lat:-18.92,lng:47.52}, +{name:"Lilongwe",country:"Malawi",iso2:"MW",flag:"\ud83c\uddf2\ud83c\uddfc",lat:-13.97,lng:33.78}, +{name:"Kuala Lumpur",country:"Malaysia",iso2:"MY",flag:"\ud83c\uddf2\ud83c\uddfe",lat:3.17,lng:101.7}, +{name:"Mal\u00e9",country:"Maldives",iso2:"MV",flag:"\ud83c\uddf2\ud83c\uddfb",lat:4.17,lng:73.51}, +{name:"Bamako",country:"Mali",iso2:"ML",flag:"\ud83c\uddf2\ud83c\uddf1",lat:12.65,lng:-8}, +{name:"Valletta",country:"Malta",iso2:"MT",flag:"\ud83c\uddf2\ud83c\uddf9",lat:35.88,lng:14.5}, +{name:"Majuro",country:"Marshall Islands",iso2:"MH",flag:"\ud83c\uddf2\ud83c\udded",lat:7.1,lng:171.38}, +{name:"Fort-de-France",country:"Martinique",iso2:"MQ",flag:"\ud83c\uddf2\ud83c\uddf6",lat:14.6,lng:-61.08}, +{name:"Nouakchott",country:"Mauritania",iso2:"MR",flag:"\ud83c\uddf2\ud83c\uddf7",lat:18.07,lng:-15.97}, +{name:"Port Louis",country:"Mauritius",iso2:"MU",flag:"\ud83c\uddf2\ud83c\uddfa",lat:-20.15,lng:57.48}, +{name:"Mamoudzou",country:"Mayotte",iso2:"YT",flag:"\ud83c\uddfe\ud83c\uddf9",lat:-12.78,lng:45.22}, +{name:"Mexico City",country:"Mexico",iso2:"MX",flag:"\ud83c\uddf2\ud83c\uddfd",lat:19.43,lng:-99.13}, +{name:"Palikir",country:"Micronesia",iso2:"FM",flag:"\ud83c\uddeb\ud83c\uddf2",lat:6.92,lng:158.15}, +{name:"Chi\u0219in\u0103u",country:"Moldova",iso2:"MD",flag:"\ud83c\uddf2\ud83c\udde9",lat:47.01,lng:28.9}, +{name:"Monaco",country:"Monaco",iso2:"MC",flag:"\ud83c\uddf2\ud83c\udde8",lat:43.73,lng:7.42}, +{name:"Ulan Bator",country:"Mongolia",iso2:"MN",flag:"\ud83c\uddf2\ud83c\uddf3",lat:47.92,lng:106.91}, +{name:"Podgorica",country:"Montenegro",iso2:"ME",flag:"\ud83c\uddf2\ud83c\uddea",lat:42.43,lng:19.27}, +{name:"Plymouth",country:"Montserrat",iso2:"MS",flag:"\ud83c\uddf2\ud83c\uddf8",lat:16.7,lng:-62.22}, +{name:"Rabat",country:"Morocco",iso2:"MA",flag:"\ud83c\uddf2\ud83c\udde6",lat:34.02,lng:-6.82}, +{name:"Maputo",country:"Mozambique",iso2:"MZ",flag:"\ud83c\uddf2\ud83c\uddff",lat:-25.95,lng:32.58}, +{name:"Naypyidaw",country:"Myanmar",iso2:"MM",flag:"\ud83c\uddf2\ud83c\uddf2",lat:19.76,lng:96.07}, +{name:"Windhoek",country:"Namibia",iso2:"NA",flag:"\ud83c\uddf3\ud83c\udde6",lat:-22.57,lng:17.08}, +{name:"Yaren",country:"Nauru",iso2:"NR",flag:"\ud83c\uddf3\ud83c\uddf7",lat:-0.55,lng:166.92}, +{name:"Kathmandu",country:"Nepal",iso2:"NP",flag:"\ud83c\uddf3\ud83c\uddf5",lat:27.72,lng:85.32}, +{name:"Amsterdam",country:"Netherlands",iso2:"NL",flag:"\ud83c\uddf3\ud83c\uddf1",lat:52.35,lng:4.92}, +{name:"Noum\u00e9a",country:"New Caledonia",iso2:"NC",flag:"\ud83c\uddf3\ud83c\udde8",lat:-22.27,lng:166.45}, +{name:"Wellington",country:"New Zealand",iso2:"NZ",flag:"\ud83c\uddf3\ud83c\uddff",lat:-41.3,lng:174.78}, +{name:"Managua",country:"Nicaragua",iso2:"NI",flag:"\ud83c\uddf3\ud83c\uddee",lat:12.13,lng:-86.25}, +{name:"Niamey",country:"Niger",iso2:"NE",flag:"\ud83c\uddf3\ud83c\uddea",lat:13.52,lng:2.12}, +{name:"Abuja",country:"Nigeria",iso2:"NG",flag:"\ud83c\uddf3\ud83c\uddec",lat:9.08,lng:7.53}, +{name:"Alofi",country:"Niue",iso2:"NU",flag:"\ud83c\uddf3\ud83c\uddfa",lat:-19.02,lng:-169.92}, +{name:"Kingston",country:"Norfolk Island",iso2:"NF",flag:"\ud83c\uddf3\ud83c\uddeb",lat:-29.05,lng:167.97}, +{name:"Pyongyang",country:"North Korea",iso2:"KP",flag:"\ud83c\uddf0\ud83c\uddf5",lat:39.02,lng:125.75}, +{name:"Skopje",country:"North Macedonia",iso2:"MK",flag:"\ud83c\uddf2\ud83c\uddf0",lat:42,lng:21.43}, +{name:"Saipan",country:"Northern Mariana Islands",iso2:"MP",flag:"\ud83c\uddf2\ud83c\uddf5",lat:15.2,lng:145.75}, +{name:"Oslo",country:"Norway",iso2:"NO",flag:"\ud83c\uddf3\ud83c\uddf4",lat:59.92,lng:10.75}, +{name:"Muscat",country:"Oman",iso2:"OM",flag:"\ud83c\uddf4\ud83c\uddf2",lat:23.62,lng:58.58}, +{name:"Islamabad",country:"Pakistan",iso2:"PK",flag:"\ud83c\uddf5\ud83c\uddf0",lat:33.68,lng:73.05}, +{name:"Ngerulmud",country:"Palau",iso2:"PW",flag:"\ud83c\uddf5\ud83c\uddfc",lat:7.5,lng:134.62}, +{name:"Ramallah",country:"Palestine",iso2:"PS",flag:"\ud83c\uddf5\ud83c\uddf8",lat:31.9,lng:35.2}, +{name:"Panama City",country:"Panama",iso2:"PA",flag:"\ud83c\uddf5\ud83c\udde6",lat:8.97,lng:-79.53}, +{name:"Port Moresby",country:"Papua New Guinea",iso2:"PG",flag:"\ud83c\uddf5\ud83c\uddec",lat:-9.45,lng:147.18}, +{name:"Asunci\u00f3n",country:"Paraguay",iso2:"PY",flag:"\ud83c\uddf5\ud83c\uddfe",lat:-25.28,lng:-57.57}, +{name:"Lima",country:"Peru",iso2:"PE",flag:"\ud83c\uddf5\ud83c\uddea",lat:-12.05,lng:-77.05}, +{name:"Manila",country:"Philippines",iso2:"PH",flag:"\ud83c\uddf5\ud83c\udded",lat:14.6,lng:120.97}, +{name:"Adamstown",country:"Pitcairn Islands",iso2:"PN",flag:"\ud83c\uddf5\ud83c\uddf3",lat:-25.07,lng:-130.08}, +{name:"Warsaw",country:"Poland",iso2:"PL",flag:"\ud83c\uddf5\ud83c\uddf1",lat:52.25,lng:21}, +{name:"Lisbon",country:"Portugal",iso2:"PT",flag:"\ud83c\uddf5\ud83c\uddf9",lat:38.72,lng:-9.13}, +{name:"San Juan",country:"Puerto Rico",iso2:"PR",flag:"\ud83c\uddf5\ud83c\uddf7",lat:18.47,lng:-66.12}, +{name:"Doha",country:"Qatar",iso2:"QA",flag:"\ud83c\uddf6\ud83c\udde6",lat:25.28,lng:51.53}, +{name:"Brazzaville",country:"Republic of the Congo",iso2:"CG",flag:"\ud83c\udde8\ud83c\uddec",lat:-4.25,lng:15.28}, +{name:"Bucharest",country:"Romania",iso2:"RO",flag:"\ud83c\uddf7\ud83c\uddf4",lat:44.43,lng:26.1}, +{name:"Moscow",country:"Russia",iso2:"RU",flag:"\ud83c\uddf7\ud83c\uddfa",lat:55.75,lng:37.6}, +{name:"Kigali",country:"Rwanda",iso2:"RW",flag:"\ud83c\uddf7\ud83c\uddfc",lat:-1.95,lng:30.05}, +{name:"Saint-Denis",country:"R\u00e9union",iso2:"RE",flag:"\ud83c\uddf7\ud83c\uddea",lat:-20.88,lng:55.45}, +{name:"Gustavia",country:"Saint Barth\u00e9lemy",iso2:"BL",flag:"\ud83c\udde7\ud83c\uddf1",lat:17.88,lng:-62.85}, +{name:"Jamestown",country:"Saint Helena, Ascension and Tristan da Cunha",iso2:"SH",flag:"\ud83c\uddf8\ud83c\udded",lat:-15.93,lng:-5.72}, +{name:"Basseterre",country:"Saint Kitts and Nevis",iso2:"KN",flag:"\ud83c\uddf0\ud83c\uddf3",lat:17.3,lng:-62.72}, +{name:"Castries",country:"Saint Lucia",iso2:"LC",flag:"\ud83c\uddf1\ud83c\udde8",lat:14,lng:-61}, +{name:"Marigot",country:"Saint Martin",iso2:"MF",flag:"\ud83c\uddf2\ud83c\uddeb",lat:18.07,lng:-63.08}, +{name:"Saint-Pierre",country:"Saint Pierre and Miquelon",iso2:"PM",flag:"\ud83c\uddf5\ud83c\uddf2",lat:46.77,lng:-56.18}, +{name:"Kingstown",country:"Saint Vincent and the Grenadines",iso2:"VC",flag:"\ud83c\uddfb\ud83c\udde8",lat:13.13,lng:-61.22}, +{name:"Apia",country:"Samoa",iso2:"WS",flag:"\ud83c\uddfc\ud83c\uddf8",lat:-13.82,lng:-171.77}, +{name:"City of San Marino",country:"San Marino",iso2:"SM",flag:"\ud83c\uddf8\ud83c\uddf2",lat:43.94,lng:12.45}, +{name:"Riyadh",country:"Saudi Arabia",iso2:"SA",flag:"\ud83c\uddf8\ud83c\udde6",lat:24.65,lng:46.7}, +{name:"Dakar",country:"Senegal",iso2:"SN",flag:"\ud83c\uddf8\ud83c\uddf3",lat:14.73,lng:-17.63}, +{name:"Belgrade",country:"Serbia",iso2:"RS",flag:"\ud83c\uddf7\ud83c\uddf8",lat:44.83,lng:20.5}, +{name:"Victoria",country:"Seychelles",iso2:"SC",flag:"\ud83c\uddf8\ud83c\udde8",lat:-4.62,lng:55.45}, +{name:"Freetown",country:"Sierra Leone",iso2:"SL",flag:"\ud83c\uddf8\ud83c\uddf1",lat:8.48,lng:-13.23}, +{name:"Singapore",country:"Singapore",iso2:"SG",flag:"\ud83c\uddf8\ud83c\uddec",lat:1.28,lng:103.85}, +{name:"Philipsburg",country:"Sint Maarten",iso2:"SX",flag:"\ud83c\uddf8\ud83c\uddfd",lat:18.02,lng:-63.03}, +{name:"Bratislava",country:"Slovakia",iso2:"SK",flag:"\ud83c\uddf8\ud83c\uddf0",lat:48.15,lng:17.12}, +{name:"Ljubljana",country:"Slovenia",iso2:"SI",flag:"\ud83c\uddf8\ud83c\uddee",lat:46.05,lng:14.52}, +{name:"Honiara",country:"Solomon Islands",iso2:"SB",flag:"\ud83c\uddf8\ud83c\udde7",lat:-9.43,lng:159.95}, +{name:"Mogadishu",country:"Somalia",iso2:"SO",flag:"\ud83c\uddf8\ud83c\uddf4",lat:2.07,lng:45.33}, +{name:"Pretoria",country:"South Africa",iso2:"ZA",flag:"\ud83c\uddff\ud83c\udde6",lat:-25.7,lng:28.22}, +{name:"King Edward Point",country:"South Georgia",iso2:"GS",flag:"\ud83c\uddec\ud83c\uddf8",lat:-54.28,lng:-36.5}, +{name:"Seoul",country:"South Korea",iso2:"KR",flag:"\ud83c\uddf0\ud83c\uddf7",lat:37.55,lng:126.98}, +{name:"Juba",country:"South Sudan",iso2:"SS",flag:"\ud83c\uddf8\ud83c\uddf8",lat:4.85,lng:31.62}, +{name:"Madrid",country:"Spain",iso2:"ES",flag:"\ud83c\uddea\ud83c\uddf8",lat:40.4,lng:-3.68}, +{name:"Sri Jayawardenepura Kotte",country:"Sri Lanka",iso2:"LK",flag:"\ud83c\uddf1\ud83c\uddf0",lat:6.89,lng:79.9}, +{name:"Khartoum",country:"Sudan",iso2:"SD",flag:"\ud83c\uddf8\ud83c\udde9",lat:15.6,lng:32.53}, +{name:"Paramaribo",country:"Suriname",iso2:"SR",flag:"\ud83c\uddf8\ud83c\uddf7",lat:5.83,lng:-55.17}, +{name:"Longyearbyen",country:"Svalbard and Jan Mayen",iso2:"SJ",flag:"\ud83c\uddf8\ud83c\uddef",lat:78.22,lng:15.63}, +{name:"Stockholm",country:"Sweden",iso2:"SE",flag:"\ud83c\uddf8\ud83c\uddea",lat:59.33,lng:18.05}, +{name:"Bern",country:"Switzerland",iso2:"CH",flag:"\ud83c\udde8\ud83c\udded",lat:46.92,lng:7.47}, +{name:"Damascus",country:"Syria",iso2:"SY",flag:"\ud83c\uddf8\ud83c\uddfe",lat:33.5,lng:36.3}, +{name:"S\u00e3o Tom\u00e9",country:"S\u00e3o Tom\u00e9 and Pr\u00edncipe",iso2:"ST",flag:"\ud83c\uddf8\ud83c\uddf9",lat:0.34,lng:6.73}, +{name:"Taipei",country:"Taiwan",iso2:"TW",flag:"\ud83c\uddf9\ud83c\uddfc",lat:25.03,lng:121.52}, +{name:"Dushanbe",country:"Tajikistan",iso2:"TJ",flag:"\ud83c\uddf9\ud83c\uddef",lat:38.55,lng:68.77}, +{name:"Dodoma",country:"Tanzania",iso2:"TZ",flag:"\ud83c\uddf9\ud83c\uddff",lat:-6.16,lng:35.75}, +{name:"Bangkok",country:"Thailand",iso2:"TH",flag:"\ud83c\uddf9\ud83c\udded",lat:13.75,lng:100.52}, +{name:"Dili",country:"Timor-Leste",iso2:"TL",flag:"\ud83c\uddf9\ud83c\uddf1",lat:-8.58,lng:125.6}, +{name:"Lom\u00e9",country:"Togo",iso2:"TG",flag:"\ud83c\uddf9\ud83c\uddec",lat:6.14,lng:1.21}, +{name:"Fakaofo",country:"Tokelau",iso2:"TK",flag:"\ud83c\uddf9\ud83c\uddf0",lat:-9.38,lng:-171.22}, +{name:"Nuku\u02bbalofa",country:"Tonga",iso2:"TO",flag:"\ud83c\uddf9\ud83c\uddf4",lat:-21.13,lng:-175.2}, +{name:"Port of Spain",country:"Trinidad and Tobago",iso2:"TT",flag:"\ud83c\uddf9\ud83c\uddf9",lat:10.65,lng:-61.52}, +{name:"Tunis",country:"Tunisia",iso2:"TN",flag:"\ud83c\uddf9\ud83c\uddf3",lat:36.8,lng:10.18}, +{name:"Ankara",country:"Turkey",iso2:"TR",flag:"\ud83c\uddf9\ud83c\uddf7",lat:39.93,lng:32.87}, +{name:"Ashgabat",country:"Turkmenistan",iso2:"TM",flag:"\ud83c\uddf9\ud83c\uddf2",lat:37.95,lng:58.38}, +{name:"Cockburn Town",country:"Turks and Caicos Islands",iso2:"TC",flag:"\ud83c\uddf9\ud83c\udde8",lat:21.46,lng:-71.14}, +{name:"Funafuti",country:"Tuvalu",iso2:"TV",flag:"\ud83c\uddf9\ud83c\uddfb",lat:-8.52,lng:179.22}, +{name:"Kampala",country:"Uganda",iso2:"UG",flag:"\ud83c\uddfa\ud83c\uddec",lat:0.32,lng:32.55}, +{name:"Kyiv",country:"Ukraine",iso2:"UA",flag:"\ud83c\uddfa\ud83c\udde6",lat:50.43,lng:30.52}, +{name:"Abu Dhabi",country:"United Arab Emirates",iso2:"AE",flag:"\ud83c\udde6\ud83c\uddea",lat:24.47,lng:54.37}, +{name:"London",country:"United Kingdom",iso2:"GB",flag:"\ud83c\uddec\ud83c\udde7",lat:51.5,lng:-0.08}, +{name:"Washington, D.C.",country:"United States",iso2:"US",flag:"\ud83c\uddfa\ud83c\uddf8",lat:38.89,lng:-77.05}, +{name:"Charlotte Amalie",country:"United States Virgin Islands",iso2:"VI",flag:"\ud83c\uddfb\ud83c\uddee",lat:18.35,lng:-64.93}, +{name:"Montevideo",country:"Uruguay",iso2:"UY",flag:"\ud83c\uddfa\ud83c\uddfe",lat:-34.85,lng:-56.17}, +{name:"Tashkent",country:"Uzbekistan",iso2:"UZ",flag:"\ud83c\uddfa\ud83c\uddff",lat:41.32,lng:69.25}, +{name:"Port Vila",country:"Vanuatu",iso2:"VU",flag:"\ud83c\uddfb\ud83c\uddfa",lat:-17.73,lng:168.32}, +{name:"Vatican City",country:"Vatican City",iso2:"VA",flag:"\ud83c\uddfb\ud83c\udde6",lat:41.9,lng:12.45}, +{name:"Caracas",country:"Venezuela",iso2:"VE",flag:"\ud83c\uddfb\ud83c\uddea",lat:10.48,lng:-66.87}, +{name:"Hanoi",country:"Vietnam",iso2:"VN",flag:"\ud83c\uddfb\ud83c\uddf3",lat:21.03,lng:105.85}, +{name:"Mata-Utu",country:"Wallis and Futuna",iso2:"WF",flag:"\ud83c\uddfc\ud83c\uddeb",lat:-13.95,lng:-171.93}, +{name:"El Aai\u00fan",country:"Western Sahara",iso2:"EH",flag:"\ud83c\uddea\ud83c\udded",lat:27.15,lng:-13.20}, +{name:"Sana'a",country:"Yemen",iso2:"YE",flag:"\ud83c\uddfe\ud83c\uddea",lat:15.37,lng:44.19}, +{name:"Lusaka",country:"Zambia",iso2:"ZM",flag:"\ud83c\uddff\ud83c\uddf2",lat:-15.42,lng:28.28}, +{name:"Harare",country:"Zimbabwe",iso2:"ZW",flag:"\ud83c\uddff\ud83c\uddfc",lat:-17.82,lng:31.03}, +{name:"Mariehamn",country:"\u00c5land Islands",iso2:"AX",flag:"\ud83c\udde6\ud83c\uddfd",lat:60.12,lng:19.9} +]; let currentRegion = 'world'; let flatSvg, flatProjection, flatPath, flatG, flatZoom, flatW, flatH; const signalGuideItems = [ @@ -610,18 +904,22 @@ const newsCount=(D.news||[]).length; const conflictEvents = D.acled?.totalEvents || 0; const conflictFatal = D.acled?.totalFatalities || 0; - const layers=[ - {name:t('layers.airActivity','Air Activity'),count:totalAir,dot:'air',sub:`${D.air.length} ${t('layers.theaters','theaters')}`}, - {name:t('layers.thermalSpikes','Thermal Spikes'),count:totalThermal.toLocaleString(),dot:'thermal',sub:`${totalNight.toLocaleString()} ${t('layers.nightDet','night det.')}`}, - {name:t('layers.sdrCoverage','SDR Coverage'),count:D.sdr.total,dot:'sdr',sub:`${D.sdr.online} ${t('layers.online','online')}`}, - {name:t('layers.maritimeWatch','Maritime Watch'),count:D.chokepoints.length,dot:'maritime',sub:t('layers.chokepoints','chokepoints')}, - {name:t('layers.nuclearSites','Nuclear Sites'),count:D.nuke.length,dot:'nuke',sub:t('layers.monitors','monitors')}, - {name:t('layers.conflictEvents','Conflict Events'),count:conflictEvents,dot:'thermal',sub:`${conflictFatal.toLocaleString()} ${t('layers.fatalities','fatalities')}`}, - {name:t('layers.healthWatch','Health Watch'),count:D.who.length,dot:'health',sub:t('layers.whoAlerts','WHO alerts')}, - {name:t('layers.worldNews','World News'),count:newsCount,dot:'news',sub:t('layers.rssGeolocated','RSS geolocated')}, - {name:t('layers.osintFeed','OSINT Feed'),count:D.tg.posts,dot:'incident',sub:`${D.tg.urgent.length} ${t('badges.urgent','urgent').toLowerCase()}`}, - {name:t('layers.spaceActivity','Satellites'),count:D.space?.militarySats||0,dot:'space',sub:`${D.space?.totalNewObjects||0} ${t('space.newLast30d','new (30d)')}`} - ]; + const layerData = { + air: {count:totalAir, sub:`${D.air.length} ${t('layers.theaters','theaters')}`}, + thermal: {count:totalThermal.toLocaleString(), sub:`${totalNight.toLocaleString()} ${t('layers.nightDet','night det.')}`}, + sdr: {count:D.sdr.total, sub:`${D.sdr.online} ${t('layers.online','online')}`}, + maritime: {count:D.chokepoints.length, sub:t('layers.chokepoints','chokepoints')}, + nuke: {count:D.nuke.length, sub:t('layers.monitors','monitors')}, + acled: {count:conflictEvents, sub:`${conflictFatal.toLocaleString()} ${t('layers.fatalities','fatalities')}`}, + who: {count:D.who.length, sub:t('layers.whoAlerts','WHO alerts')}, + news: {count:newsCount, sub:t('layers.rssGeolocated','RSS geolocated')}, + osint: {count:D.tg.posts, sub:`${D.tg.urgent.length} ${t('badges.urgent','urgent').toLowerCase()}`}, + space: {count:D.space?.militarySats||0, sub:`${D.space?.totalNewObjects||0} ${t('space.newLast30d','new (30d)')}`}, + noaa: {count:D.noaa?.totalAlerts||0, sub:t('layers.severeWeather','severe weather')}, + epa: {count:D.epa?.totalReadings||0, sub:t('layers.radiationNet','radiation net')}, + gdelt: {count:(D.gdelt?.geoPoints||[]).length, sub:t('layers.globalNewsGeo','global news geo')}, + capitals: {count:CAPITALS.length, sub:t('layers.worldCapitals','world capitals')}, + }; const allNormal=D.nuke.every(s=>!s.anom); const nukeHtml=D.nuke.map(s=>`
${s.site}${s.n>0?(s.cpm?.toFixed(1)||'--')+' CPM':'No data'}
`).join(''); const vix=D.fred.find(f=>f.id==='VIXCLS'); @@ -634,7 +932,11 @@ document.getElementById('leftRail').innerHTML=`

${t('panels.sensorGrid','Sensor Grid')}

${t('badges.live','LIVE')}
- ${layers.map(l=>`
${l.name}
${l.sub}
${l.count}
`).join('')} + ${LAYERS.map(l=>{ + const vis = layerVisibility[l.id]; + const d = layerData[l.id] || {count:0, sub:''}; + return `
${l.label}
${vis ? d.sub : 'hidden'}
${vis ? d.count : '\u2014'}
👁️
`; + }).join('')}

${t('panels.nuclearWatch','Nuclear Watch')}

${t('badges.radiation','RADIATION')}
@@ -680,10 +982,13 @@ } function renderMapLegend(){ + const legendMap = {air:'Air Traffic',thermal:'Thermal/Fire',acled:'Conflict',sdr:'SDR Receiver',nuke:'Nuclear Site',maritime:'Chokepoint',osint:'OSINT Event',who:'Health Alert',news:'World News',noaa:'Weather Alert',epa:'EPA RadNet',space:'Space Station',gdelt:'GDELT Event',capitals:'World Capital'}; document.getElementById('mapLegend').innerHTML= - [{c:'#64f0c8',l:t('map.airTraffic','Air Traffic')},{c:'#ff5f63',l:t('map.thermalFire','Thermal/Fire')},{c:'rgba(255,120,80,0.8)',l:t('map.conflict','Conflict')},{c:'#44ccff',l:t('map.sdrReceiver','SDR Receiver')}, - {c:'#ffe082',l:t('map.nuclearSite','Nuclear Site')},{c:'#b388ff',l:t('map.chokepoint','Chokepoint')},{c:'#ffb84c',l:t('map.osintEvent','OSINT Event')},{c:'#69f0ae',l:t('map.healthAlert','Health Alert')},{c:'#81d4fa',l:t('map.worldNews','World News')},{c:'#ff9800',l:t('map.weatherAlert','Weather Alert')},{c:'#cddc39',l:t('map.epaRadNet','EPA RadNet')},{c:'#ffffff',l:t('map.spaceStation','Space Station')},{c:'#6495ed',l:t('map.gdeltEvent','GDELT Event')}] - .map(x=>`
${x.l}
`).join(''); + LAYERS.map(l=>{ + const vis = layerVisibility[l.id]; + const label = legendMap[l.id] || l.label; + return `
${label}
`; + }).join(''); } function initMap(){ @@ -842,7 +1147,7 @@ // === Air hotspots (green) === const airCoords=[{lat:30,lon:44},{lat:24,lon:120},{lat:49,lon:32},{lat:57,lon:24},{lat:14,lon:114},{lat:37,lon:127},{lat:25,lon:-80},{lat:4,lon:2},{lat:-34,lon:18},{lat:10,lon:51}]; - if(flightsVisible) D.air.forEach((a,i)=>{ + if(layerVisibility.air) D.air.forEach((a,i)=>{ const c=airCoords[i]; if(!c) return; points.push({ lat:c.lat, lng:c.lon, size:0.25+a.total/200, alt:0.015, @@ -855,7 +1160,7 @@ }); // === Thermal/fire (red) === - D.thermal.forEach(t=>{ + if(layerVisibility.thermal) D.thermal.forEach(t=>{ t.fires.forEach(f=>{ points.push({ lat:f.lat, lng:f.lon, size:0.12+Math.min(f.frp/200,0.3), alt:0.008, @@ -867,7 +1172,7 @@ }); // === Maritime chokepoints (purple) === - D.chokepoints.forEach(cp=>{ + if(layerVisibility.maritime) D.chokepoints.forEach(cp=>{ points.push({ lat:cp.lat, lng:cp.lon, size:0.35, alt:0.02, color:'rgba(179,136,255,0.8)', type:'maritime', priority:1, @@ -878,7 +1183,7 @@ // === Nuclear sites (yellow) === const nukeCoords=[{lat:47.5,lon:34.6},{lat:51.4,lon:30.1},{lat:28.8,lon:50.9},{lat:39.8,lon:125.8},{lat:37.4,lon:141},{lat:31.0,lon:35.1}]; - D.nuke.forEach((n,i)=>{ + if(layerVisibility.nuke) D.nuke.forEach((n,i)=>{ const c=nukeCoords[i]; if(!c) return; points.push({ lat:c.lat, lng:c.lon, size:0.3, alt:0.012, @@ -889,7 +1194,7 @@ }); // === SDR receivers (cyan) === - D.sdr.zones.forEach(z=>{ + if(layerVisibility.sdr) D.sdr.zones.forEach(z=>{ z.receivers.forEach(r=>{ points.push({ lat:r.lat, lng:r.lon, size:0.15, alt:0.005, @@ -902,7 +1207,7 @@ // === OSINT events from Telegram (orange) === const osintGeo=[{lat:45,lon:41,idx:0},{lat:48,lon:37,idx:1},{lat:48.5,lon:37.5,idx:2},{lat:45,lon:40.2,idx:3},{lat:50.6,lon:36.6,idx:5},{lat:48.5,lon:35,idx:6}]; - osintGeo.forEach(o=>{ + if(layerVisibility.osint) osintGeo.forEach(o=>{ const post=D.tg.urgent[o.idx]; if(!post) return; points.push({ lat:o.lat, lng:o.lon, size:0.3, alt:0.018, @@ -914,7 +1219,7 @@ // === WHO health alerts (green) === const whoGeo=[{lat:0.3,lon:32.6},{lat:-6.2,lon:106.8},{lat:-4.3,lon:15.3},{lat:35,lon:105},{lat:12.5,lon:105},{lat:35,lon:105},{lat:28,lon:84},{lat:24,lon:45},{lat:30,lon:70},{lat:-0.8,lon:11.6}]; - D.who.slice(0,10).forEach((w,i)=>{ + if(layerVisibility.who) D.who.slice(0,10).forEach((w,i)=>{ const c=whoGeo[i]; if(!c) return; points.push({ lat:c.lat, lng:c.lon, size:0.25, alt:0.01, @@ -924,7 +1229,7 @@ }); // === News markers (light blue) === - (D.news||[]).forEach(n=>{ + if(layerVisibility.news) (D.news||[]).forEach(n=>{ points.push({ lat:n.lat, lng:n.lon, size:0.2, alt:0.008, color:'rgba(129,212,250,0.7)', type:'news', priority:3, @@ -934,7 +1239,7 @@ }); // === NOAA severe weather alerts (orange) === - (D.noaa?.alerts||[]).forEach(a=>{ + if(layerVisibility.noaa) (D.noaa?.alerts||[]).forEach(a=>{ points.push({ lat:a.lat, lng:a.lon, size:0.22, alt:0.01, color:'rgba(255,152,0,0.8)', type:'weather', priority:2, @@ -944,7 +1249,7 @@ }); // === EPA RadNet stations (lime green) === - (D.epa?.stations||[]).forEach(s=>{ + if(layerVisibility.epa) (D.epa?.stations||[]).forEach(s=>{ points.push({ lat:s.lat, lng:s.lon, size:0.18, alt:0.006, color:'rgba(205,220,57,0.7)', type:'radiation', priority:3, @@ -954,7 +1259,7 @@ }); // === ISS + Space Stations (bright white, pulsing) === - (D.space?.stationPositions||[]).forEach(s=>{ + if(layerVisibility.space) (D.space?.stationPositions||[]).forEach(s=>{ points.push({ lat:s.lat, lng:s.lon, size:0.4, alt:0.04, color:'rgba(255,255,255,0.95)', type:'space', priority:1, @@ -965,7 +1270,7 @@ }); // === GDELT geo events (steel blue) === - (D.gdelt?.geoPoints||[]).forEach(g=>{ + if(layerVisibility.gdelt) (D.gdelt?.geoPoints||[]).forEach(g=>{ points.push({ lat:g.lat, lng:g.lon, size:0.15+Math.min(g.count/50,0.2), alt:0.007, color:'rgba(100,149,237,0.6)', type:'gdelt', priority:3, @@ -974,12 +1279,23 @@ }); }); + // === World Capitals (gold) === + if(layerVisibility.capitals) CAPITALS.forEach(c=>{ + points.push({ + lat:c.lat, lng:c.lng, size:0.12, alt:0.005, + color:'rgba(255,213,79,0.7)', type:'capital', priority:3, + popHead:c.flag+' '+c.name, + popMeta:c.country+' ('+c.iso2+')', + popText:'Capital city' + }); + }); + // Set points on globe globe.pointsData(points); globe.labelsData(labels); // === ACLED CONFLICT EVENTS (pulsing rings) === - const conflictRings = (D.acled?.deadliestEvents || []).filter(e => e.lat && e.lon).map(e => { + const conflictRings = !layerVisibility.acled ? [] : (D.acled?.deadliestEvents || []).filter(e => e.lat && e.lon).map(e => { const logFatal = Math.log2(Math.max(e.fatalities, 1)); return { lat: e.lat, lng: e.lon, @@ -994,7 +1310,7 @@ // === FLIGHT CORRIDORS (3D arcs) === const arcs = []; - if(flightsVisible){ + if(layerVisibility.air){ const airCoordsFlight = [ {region:'Middle East',lat:30,lon:44}, {region:'Taiwan Strait',lat:24,lon:120}, {region:'Ukraine Region',lat:49,lon:32}, {region:'Baltic Region',lat:57,lon:24}, @@ -1095,33 +1411,6 @@ } function closePopup(){document.getElementById('mapPopup').classList.remove('show')} -// === MAP CONTROLS === -function toggleFlights() { - flightsVisible = !flightsVisible; - const btn = document.getElementById('flightToggle'); - btn.classList.toggle('off', !flightsVisible); - if(isFlat){ - if(flatG){ - flatG.selectAll('*').remove(); - drawFlatMap(); - } - return; - } - if(!globe){ - return; - } - if(flightsVisible) { - plotMarkers(); // re-render with arcs - } else { - globe.arcsData([]); // hide arcs - // Remove air-type points - const pts = globe.pointsData().filter(p => p.type !== 'air'); - globe.pointsData(pts); - const lbls = globe.labelsData().filter(l => l.text && !l.text.match(/\d+$/)); - globe.labelsData(lbls); - } -} - // === FLAT/GLOBE TOGGLE === const flatRegionBounds = { world:[[-180,-60],[180,80]], americas:[[-130,10],[-60,55]], europe:[[-12,34],[45,72]], @@ -1207,7 +1496,7 @@ }; // Air const airCoords=[{lat:30,lon:44},{lat:24,lon:120},{lat:49,lon:32},{lat:57,lon:24},{lat:14,lon:114},{lat:37,lon:127},{lat:25,lon:-80},{lat:4,lon:2},{lat:-34,lon:18},{lat:10,lon:51}]; - if(flightsVisible){ + if(layerVisibility.air){ D.air.forEach((a,i)=>{ const c=airCoords[i];if(!c)return; const g=addPt(c.lat,c.lon,4+a.total/40,'rgba(100,240,200,0.7)','rgba(100,240,200,0.3)', @@ -1216,12 +1505,12 @@ }); } // Thermal - D.thermal.forEach(t=>t.fires.forEach(f=>{ + if(layerVisibility.thermal) D.thermal.forEach(t=>t.fires.forEach(f=>{ addPt(f.lat,f.lon,2+Math.min(f.frp/50,5),'rgba(255,95,99,0.6)','rgba(255,95,99,0.2)', ev=>showPopup(ev,'Thermal',`${t.region}
FRP: ${f.frp.toFixed(1)} MW`,'FIRMS'),3); })); // Chokepoints - D.chokepoints.forEach(cp=>{ + if(layerVisibility.maritime) D.chokepoints.forEach(cp=>{ const[x,y]=proj([cp.lon,cp.lat]);if(!x||!y)return; const g=mg.append('g').attr('transform',`translate(${x},${y})`).style('cursor','pointer').attr('data-priority',1) .on('click',ev=>{ev.stopPropagation();showPopup(ev,cp.label,cp.note,'Maritime')}); @@ -1230,30 +1519,32 @@ }); // Nuclear const nukeCoords=[{lat:47.5,lon:34.6},{lat:51.4,lon:30.1},{lat:28.8,lon:50.9},{lat:39.8,lon:125.8},{lat:37.4,lon:141},{lat:31.0,lon:35.1}]; - D.nuke.forEach((n,i)=>{const c=nukeCoords[i];if(!c)return;addPt(c.lat,c.lon,4,'rgba(255,224,130,0.7)','rgba(255,224,130,0.3)',ev=>showPopup(ev,n.site,`CPM: ${n.cpm?.toFixed(1)||'--'}`,'Radiation'),2)}); + if(layerVisibility.nuke) D.nuke.forEach((n,i)=>{const c=nukeCoords[i];if(!c)return;addPt(c.lat,c.lon,4,'rgba(255,224,130,0.7)','rgba(255,224,130,0.3)',ev=>showPopup(ev,n.site,`CPM: ${n.cpm?.toFixed(1)||'--'}`,'Radiation'),2)}); // SDR - D.sdr.zones.forEach(z=>z.receivers.forEach(r=>{addPt(r.lat,r.lon,2.5,'rgba(68,204,255,0.5)','rgba(68,204,255,0.2)',ev=>showPopup(ev,'SDR',`${r.name}
${z.region}`,'KiwiSDR'),3)})); + if(layerVisibility.sdr) D.sdr.zones.forEach(z=>z.receivers.forEach(r=>{addPt(r.lat,r.lon,2.5,'rgba(68,204,255,0.5)','rgba(68,204,255,0.2)',ev=>showPopup(ev,'SDR',`${r.name}
${z.region}`,'KiwiSDR'),3)})); // OSINT const osintGeo=[{lat:45,lon:41,idx:0},{lat:48,lon:37,idx:1},{lat:48.5,lon:37.5,idx:2},{lat:45,lon:40.2,idx:3},{lat:50.6,lon:36.6,idx:5},{lat:48.5,lon:35,idx:6}]; - osintGeo.forEach(o=>{const p=D.tg.urgent[o.idx];if(!p)return;addPt(o.lat,o.lon,4,'rgba(255,184,76,0.7)','rgba(255,184,76,0.3)',ev=>showPopup(ev,(p.channel||'').toUpperCase(),cleanText(p.text?.substring(0,200)||''),`${p.views||'?'} views`),2)}); + if(layerVisibility.osint) osintGeo.forEach(o=>{const p=D.tg.urgent[o.idx];if(!p)return;addPt(o.lat,o.lon,4,'rgba(255,184,76,0.7)','rgba(255,184,76,0.3)',ev=>showPopup(ev,(p.channel||'').toUpperCase(),cleanText(p.text?.substring(0,200)||''),`${p.views||'?'} views`),2)}); // WHO const whoGeo=[{lat:0.3,lon:32.6},{lat:-6.2,lon:106.8},{lat:-4.3,lon:15.3},{lat:35,lon:105},{lat:12.5,lon:105},{lat:35,lon:105},{lat:28,lon:84},{lat:24,lon:45},{lat:30,lon:70},{lat:-0.8,lon:11.6}]; - D.who.slice(0,10).forEach((w,i)=>{const c=whoGeo[i];if(!c)return;addPt(c.lat,c.lon,3.5,'rgba(105,240,174,0.6)','rgba(105,240,174,0.2)',ev=>showPopup(ev,w.title,w.summary||'','WHO'),2)}); + if(layerVisibility.who) D.who.slice(0,10).forEach((w,i)=>{const c=whoGeo[i];if(!c)return;addPt(c.lat,c.lon,3.5,'rgba(105,240,174,0.6)','rgba(105,240,174,0.2)',ev=>showPopup(ev,w.title,w.summary||'','WHO'),2)}); // News - (D.news||[]).forEach(n=>{addPt(n.lat,n.lon,3,'rgba(129,212,250,0.6)','rgba(129,212,250,0.2)',ev=>showPopup(ev,n.source+' NEWS',cleanText(n.title),n.region),3)}); + if(layerVisibility.news) (D.news||[]).forEach(n=>{addPt(n.lat,n.lon,3,'rgba(129,212,250,0.6)','rgba(129,212,250,0.2)',ev=>showPopup(ev,n.source+' NEWS',cleanText(n.title),n.region),3)}); // NOAA weather - (D.noaa?.alerts||[]).forEach(a=>{addPt(a.lat,a.lon,4,'rgba(255,152,0,0.7)','rgba(255,152,0,0.3)',ev=>showPopup(ev,a.event,a.headline||'','NOAA/NWS'),2)}); + if(layerVisibility.noaa) (D.noaa?.alerts||[]).forEach(a=>{addPt(a.lat,a.lon,4,'rgba(255,152,0,0.7)','rgba(255,152,0,0.3)',ev=>showPopup(ev,a.event,a.headline||'','NOAA/NWS'),2)}); // EPA RadNet - (D.epa?.stations||[]).forEach(s=>{addPt(s.lat,s.lon,3,'rgba(205,220,57,0.6)','rgba(205,220,57,0.2)',ev=>showPopup(ev,'RadNet: '+s.location,`${s.analyte||'--'}: ${s.result||'--'} ${s.unit||''}`,'EPA'),3)}); + if(layerVisibility.epa) (D.epa?.stations||[]).forEach(s=>{addPt(s.lat,s.lon,3,'rgba(205,220,57,0.6)','rgba(205,220,57,0.2)',ev=>showPopup(ev,'RadNet: '+s.location,`${s.analyte||'--'}: ${s.result||'--'} ${s.unit||''}`,'EPA'),3)}); // Space stations - (D.space?.stationPositions||[]).forEach(s=>{ + if(layerVisibility.space) (D.space?.stationPositions||[]).forEach(s=>{ const g=addPt(s.lat,s.lon,5,'rgba(255,255,255,0.9)','rgba(255,255,255,0.4)',ev=>showPopup(ev,s.name,'Orbital position estimate','Space Station'),1); if(g) g.append('text').attr('class','marker-label').attr('x',8).attr('y',3).attr('fill','rgba(255,255,255,0.7)').attr('font-size','8px').attr('font-family','var(--mono)').text(s.name.split('(')[0].trim()); }); // GDELT geo events - (D.gdelt?.geoPoints||[]).forEach(g=>{addPt(g.lat,g.lon,2.5,'rgba(100,149,237,0.5)','rgba(100,149,237,0.2)',ev=>showPopup(ev,'GDELT Event',g.name||'','GDELT · '+g.count+' reports'),3)}); + if(layerVisibility.gdelt) (D.gdelt?.geoPoints||[]).forEach(g=>{addPt(g.lat,g.lon,2.5,'rgba(100,149,237,0.5)','rgba(100,149,237,0.2)',ev=>showPopup(ev,'GDELT Event',g.name||'','GDELT · '+g.count+' reports'),3)}); + // Capitals + if(layerVisibility.capitals) CAPITALS.forEach(c=>{addPt(c.lat,c.lng,2,'rgba(255,213,79,0.6)','rgba(255,213,79,0.25)',ev=>showPopup(ev,c.flag+' '+c.name,'Capital city',c.country+' ('+c.iso2+')'),3)}); // ACLED - (D.acled?.deadliestEvents||[]).filter(e=>e.lat&&e.lon).forEach(e=>{ + if(layerVisibility.acled) (D.acled?.deadliestEvents||[]).filter(e=>e.lat&&e.lon).forEach(e=>{ const[x,y]=proj([e.lon,e.lat]);if(!x||!y)return; const r=Math.max(4,Math.min(14,2+Math.log2(Math.max(e.fatalities,1))*1.5)); const g=mg.append('g').attr('transform',`translate(${x},${y})`).style('cursor','pointer').attr('data-priority',1) @@ -1262,7 +1553,7 @@ g.append('circle').attr('r',r*0.4).attr('fill','rgba(255,120,80,0.3)'); }); // Flight corridors - if(flightsVisible){ + if(layerVisibility.air){ const airCoordsFlight=[{lat:30,lon:44},{lat:24,lon:120},{lat:49,lon:32},{lat:57,lon:24},{lat:14,lon:114},{lat:37,lon:127},{lat:25,lon:-80},{lat:4,lon:2},{lat:-34,lon:18},{lat:10,lon:51}]; const hubs=[{lat:40.6,lon:-73.8},{lat:51.5,lon:-0.5},{lat:25.3,lon:55.4},{lat:1.4,lon:103.8},{lat:-33.9,lon:151.2},{lat:-23.4,lon:-46.5}]; const cG=flatG.append('g').attr('class','corridors-layer'); @@ -1576,7 +1867,7 @@ tl.to('#main',{opacity:1,duration:0.6},3.9); tl.call(()=>{ gsap.from('.g-panel,.topbar,.map-container',{opacity:0,y:20,scale:0.97,duration:0.5,stagger:0.06,ease:'power2.out'}); - setTimeout(()=>gsap.from('.layer-item,.site-row,.econ-row',{opacity:0,x:-12,duration:0.25,stagger:0.03,ease:'power1.out'}),500); + setTimeout(()=>gsap.from('.site-row,.econ-row',{opacity:0,x:-12,duration:0.25,stagger:0.03,ease:'power1.out'}),500); setTimeout(()=>gsap.from('.ic',{opacity:0,y:12,duration:0.25,stagger:0.03,ease:'power1.out'}),600); setTimeout(()=>gsap.from('.mc',{opacity:0,y:8,duration:0.25,stagger:0.04,ease:'power1.out'}),800); setTimeout(()=>gsap.from('.idea-card',{opacity:0,x:12,duration:0.3,stagger:0.06,ease:'power1.out'}),900);