#!/usr/bin/env php ['timeout' => 15, 'user_agent' => 'CodePressCMS/1.9.0']]); $data = @file_get_contents($topoUrl, false, $ctx); if ($data) { file_put_contents($cacheFile, $data); } } if (!file_exists($cacheFile)) { return false; } $topo = json_decode(file_get_contents($cacheFile), true); if (!$topo || empty($topo['objects']['countries']['geometries'])) { return false; } // Mapping ISO 3166-1 numeric code -> ISO 3166-1 alpha-2 code $numericToAlpha2 = [ '4' => 'AF', '8' => 'AL', '12' => 'DZ', '20' => 'AD', '24' => 'AO', '28' => 'AG', '32' => 'AR', '51' => 'AM', '36' => 'AU', '40' => 'AT', '31' => 'AZ', '44' => 'BS', '48' => 'BH', '50' => 'BD', '52' => 'BB', '112' => 'BY', '56' => 'BE', '84' => 'BZ', '204' => 'BJ', '64' => 'BT', '68' => 'BO', '70' => 'BA', '72' => 'BW', '76' => 'BR', '96' => 'BN', '100' => 'BG', '854' => 'BF', '108' => 'BI', '116' => 'KH', '120' => 'CM', '124' => 'CA', '132' => 'CV', '140' => 'CF', '148' => 'TD', '152' => 'CL', '156' => 'CN', '170' => 'CO', '174' => 'KM', '178' => 'CG', '180' => 'CD', '188' => 'CR', '384' => 'CI', '191' => 'HR', '192' => 'CU', '196' => 'CY', '203' => 'CZ', '208' => 'DK', '262' => 'DJ', '212' => 'DM', '214' => 'DO', '218' => 'EC', '818' => 'EG', '222' => 'SV', '226' => 'GQ', '232' => 'ER', '233' => 'EE', '231' => 'ET', '242' => 'FJ', '246' => 'FI', '250' => 'FR', '266' => 'GA', '270' => 'GM', '268' => 'GE', '276' => 'DE', '288' => 'GH', '300' => 'GR', '308' => 'GD', '320' => 'GT', '324' => 'GN', '624' => 'GW', '328' => 'GY', '332' => 'HT', '340' => 'HN', '348' => 'HU', '352' => 'IS', '356' => 'IN', '360' => 'ID', '364' => 'IR', '368' => 'IQ', '372' => 'IE', '376' => 'IL', '380' => 'IT', '388' => 'JM', '392' => 'JP', '400' => 'JO', '398' => 'KZ', '404' => 'KE', '296' => 'KI', '408' => 'KP', '410' => 'KR', '414' => 'KW', '417' => 'KG', '418' => 'LA', '428' => 'LV', '422' => 'LB', '426' => 'LS', '430' => 'LR', '434' => 'LY', '438' => 'LI', '440' => 'LT', '442' => 'LU', '807' => 'MK', '450' => 'MG', '454' => 'MW', '458' => 'MY', '462' => 'MV', '466' => 'ML', '470' => 'MT', '584' => 'MH', '478' => 'MR', '480' => 'MU', '484' => 'MX', '583' => 'FM', '498' => 'MD', '492' => 'MC', '496' => 'MN', '499' => 'ME', '504' => 'MA', '508' => 'MZ', '104' => 'MM', '516' => 'NA', '520' => 'NR', '524' => 'NP', '528' => 'NL', '554' => 'NZ', '558' => 'NI', '562' => 'NE', '566' => 'NG', '578' => 'NO', '512' => 'OM', '586' => 'PK', '585' => 'PW', '591' => 'PA', '598' => 'PG', '600' => 'PY', '604' => 'PE', '608' => 'PH', '616' => 'PL', '620' => 'PT', '634' => 'QA', '642' => 'RO', '643' => 'RU', '646' => 'RW', '659' => 'KN', '662' => 'LC', '670' => 'VC', '882' => 'WS', '674' => 'SM', '678' => 'ST', '682' => 'SA', '686' => 'SN', '688' => 'RS', '690' => 'SC', '694' => 'SL', '702' => 'SG', '703' => 'SK', '705' => 'SI', '90' => 'SB', '706' => 'SO', '710' => 'ZA', '728' => 'SS', '724' => 'ES', '144' => 'LK', '729' => 'SD', '740' => 'SR', '748' => 'SZ', '752' => 'SE', '756' => 'CH', '760' => 'SY', '158' => 'TW', '762' => 'TJ', '834' => 'TZ', '764' => 'TH', '626' => 'TL', '768' => 'TG', '776' => 'TO', '780' => 'TT', '788' => 'TN', '792' => 'TR', '795' => 'TM', '798' => 'TV', '800' => 'UG', '804' => 'UA', '784' => 'AE', '826' => 'GB', '840' => 'US', '858' => 'UY', '860' => 'UZ', '548' => 'VU', '862' => 'VE', '704' => 'VN', '887' => 'YE', '894' => 'ZM', '716' => 'ZW', '-99' => 'XK' ]; // Decode TopoJSON arcs $scale = $topo['transform']['scale']; $translate = $topo['transform']['translate']; $rawArcs = $topo['arcs']; $decodedArcs = []; foreach ($rawArcs as $arcIndex => $arc) { $x = 0; $y = 0; $points = []; foreach ($arc as $position) { $x += $position[0]; $y += $position[1]; // Equirectangular projection mapping to 1000x500 canvas $lon = $x * $scale[0] + $translate[0]; $lat = $y * $scale[1] + $translate[1]; $svgX = round(($lon + 180) * (1000 / 360), 2); $svgY = round((90 - $lat) * (500 / 180), 2); $points[] = [$svgX, $svgY]; } $decodedArcs[$arcIndex] = $points; } $svgPaths = []; foreach ($topo['objects']['countries']['geometries'] as $geo) { $numericId = (string)($geo['id'] ?? ''); $countryName = $geo['properties']['name'] ?? 'Unknown'; $alpha2 = $numericToAlpha2[$numericId] ?? null; if (!$alpha2) continue; $pathD = ''; $type = $geo['type']; $arcsList = ($type === 'Polygon') ? [$geo['arcs']] : (($type === 'MultiPolygon') ? $geo['arcs'] : []); foreach ($arcsList as $polygon) { foreach ($polygon as $ringIndex => $ring) { $ringPoints = []; foreach ($ring as $arcIdx) { $reversed = $arcIdx < 0; $actualIdx = $reversed ? ~$arcIdx : $arcIdx; $arcPoints = $decodedArcs[$actualIdx] ?? []; if ($reversed) { $arcPoints = array_reverse($arcPoints); } if (!empty($ringPoints)) { array_shift($arcPoints); // Skip duplicate start point } $ringPoints = array_merge($ringPoints, $arcPoints); } if (empty($ringPoints)) continue; $pathD .= 'M' . $ringPoints[0][0] . ',' . $ringPoints[0][1] . ' '; for ($p = 1; $p < count($ringPoints); $p++) { $pathD .= 'L' . $ringPoints[$p][0] . ',' . $ringPoints[$p][1] . ' '; } $pathD .= 'Z '; } } if (trim($pathD) !== '') { $svgPaths[] = sprintf( ' %s', htmlspecialchars($alpha2), htmlspecialchars($countryName), trim($pathD), htmlspecialchars($countryName) ); } } $svgContent = '' . "\n"; $svgContent .= '' . "\n"; $svgContent .= ' ' . "\n"; $svgContent .= ' ' . "\n"; $svgContent .= implode("\n", $svgPaths) . "\n"; $svgContent .= ''; file_put_contents($outPath, $svgContent); return true; } if (php_sapi_name() === 'cli') { echo "Wereldkaart SVG genereren uit Natural Earth TopoJSON...\n"; if (generateWorldMapSvg()) { echo "Wereldkaart SVG succesvol aangemaakt in public/assets/img/world-map.svg!\n"; } else { echo "Fout bij genereren van wereldkaart SVG.\n"; } }