observatory

This commit is contained in:
alma 2025-05-04 16:58:04 +02:00
parent 1ea4f0eae0
commit 1bc512af60
3 changed files with 129 additions and 65 deletions

View File

@ -96,13 +96,16 @@ export async function GET(request: Request) {
console.log('Fetching news from FastAPI server...');
const response = await fetch(`${env.NEWS_API_URL}/news?limit=12`, {
// Get limit from query params or default to 100
const limit = url.searchParams.get('limit') || '100';
const response = await fetch(`${env.NEWS_API_URL}/news?limit=${limit}`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
// Add timeout to prevent hanging
signal: AbortSignal.timeout(5000)
signal: AbortSignal.timeout(10000) // Extended timeout for larger requests
});
if (!response.ok) {

View File

@ -16,6 +16,71 @@ interface ObservatoryMapProps {
selectedCountry: string | null;
}
// Simplified map positions for common countries
const COUNTRY_POSITIONS: Record<string, [number, number]> = {
// Africa
'Sudan': [0.55, 0.45],
'Egypt': [0.57, 0.40],
'South Africa': [0.55, 0.65],
'Nigeria': [0.48, 0.50],
'Kenya': [0.59, 0.52],
'Ethiopia': [0.60, 0.47],
'Morocco': [0.45, 0.38],
'Algeria': [0.47, 0.40],
'Tunisia': [0.48, 0.36],
// Americas
'USA': [0.25, 0.35],
'New York': [0.28, 0.32],
'Canada': [0.22, 0.25],
'Mexico': [0.20, 0.40],
'Brazil': [0.35, 0.60],
'Argentina': [0.32, 0.70],
// Europe
'UK': [0.45, 0.25],
'France': [0.48, 0.30],
'Germany': [0.50, 0.28],
'Italy': [0.52, 0.33],
'Spain': [0.45, 0.33],
'Ukraine': [0.57, 0.28],
'Russia': [0.65, 0.20],
'Poland': [0.53, 0.26],
'Sweden': [0.52, 0.18],
'Norway': [0.50, 0.15],
'Finland': [0.55, 0.15],
'Greece': [0.55, 0.35],
'Netherlands': [0.48, 0.25],
'Belgium': [0.47, 0.27],
'Portugal': [0.43, 0.35],
'Switzerland': [0.49, 0.29],
'Austria': [0.51, 0.29],
// Asia
'China': [0.75, 0.35],
'India': [0.70, 0.40],
'Japan': [0.85, 0.33],
'South Korea': [0.83, 0.32],
'Indonesia': [0.78, 0.55],
'Thailand': [0.75, 0.45],
'Vietnam': [0.78, 0.43],
'Philippines': [0.82, 0.45],
'Malaysia': [0.76, 0.50],
'Singapore': [0.76, 0.52],
'Pakistan': [0.67, 0.38],
'Iran': [0.62, 0.38],
'Iraq': [0.60, 0.38],
'Saudi Arabia': [0.60, 0.42],
'Turkey': [0.58, 0.35],
'Israel': [0.58, 0.38],
'Palestine': [0.58, 0.39],
'Syria': [0.59, 0.37],
'Afghanistan': [0.65, 0.38],
// Oceania
'Australia': [0.80, 0.65]
};
export function ObservatoryMap({
countries,
onCountrySelect,
@ -70,74 +135,52 @@ export function ObservatoryMap({
ctx.stroke();
}
// Draw country markers at more fixed positions
// Calculate the top countries by news count
const topCountries = [...countries]
.sort((a, b) => b.count - a.count)
.slice(0, 15);
// Draw country markers for all countries
countries.forEach((country) => {
// Position based on country name (simplified)
let x, y;
// Very simple positioning logic
switch(country.name) {
case 'Sudan':
x = canvas.width * 0.55;
y = canvas.height * 0.45;
break;
case 'Ukraine':
x = canvas.width * 0.65;
y = canvas.height * 0.25;
break;
case 'USA':
case 'New York':
x = canvas.width * 0.25;
y = canvas.height * 0.35;
break;
case 'UK':
x = canvas.width * 0.45;
y = canvas.height * 0.25;
break;
case 'France':
x = canvas.width * 0.48;
y = canvas.height * 0.30;
break;
case 'China':
x = canvas.width * 0.75;
y = canvas.height * 0.35;
break;
case 'Russia':
x = canvas.width * 0.70;
y = canvas.height * 0.20;
break;
case 'India':
x = canvas.width * 0.70;
y = canvas.height * 0.40;
break;
case 'Brazil':
x = canvas.width * 0.35;
y = canvas.height * 0.60;
break;
case 'Australia':
x = canvas.width * 0.80;
y = canvas.height * 0.65;
break;
default:
// Random position for other countries
x = 100 + Math.random() * (canvas.width - 200);
y = 100 + Math.random() * (canvas.height - 200);
// Use predefined positions or random for others
if (COUNTRY_POSITIONS[country.name]) {
const [xRatio, yRatio] = COUNTRY_POSITIONS[country.name];
x = canvas.width * xRatio;
y = canvas.height * yRatio;
} else {
// Random position for other countries
x = 100 + Math.random() * (canvas.width - 200);
y = 100 + Math.random() * (canvas.height - 200);
}
// Size based on count (with limits)
const isTopCountry = topCountries.includes(country);
const size = Math.min(Math.max(3, country.count / 2), 8);
// Draw marker
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.arc(x, y, size, 0, Math.PI * 2);
// Simple color
ctx.fillStyle = country.name === 'Sudan' ? '#f87171' : '#cbd5e1';
// Color based on count and selection
if (country.name === selectedCountry) {
ctx.fillStyle = '#3b82f6'; // Blue when selected
} else if (isTopCountry) {
ctx.fillStyle = '#f87171'; // Red for top countries
} else {
ctx.fillStyle = '#cbd5e1'; // Gray for others
}
ctx.fill();
// Draw country name if it's Sudan or selected
if (country.name === 'Sudan' || country.name === selectedCountry) {
// Draw country name if it's a top country or selected
if (isTopCountry || country.name === selectedCountry) {
ctx.fillStyle = '#374151';
ctx.font = '12px system-ui, sans-serif';
ctx.fillText(country.name, x + 10, y + 4);
// Show count for top countries
ctx.fillText(`${country.name} (${country.count})`, x + 10, y + 4);
}
// Store coordinates for click detection
@ -154,11 +197,12 @@ export function ObservatoryMap({
// Check if a country marker was clicked
for (const country of countries) {
if (country.x && country.y) {
const size = Math.min(Math.max(3, country.count / 2), 8);
const distance = Math.sqrt(
Math.pow(x - country.x, 2) + Math.pow(y - country.y, 2)
);
if (distance <= 8) {
if (distance <= size + 2) {
onCountrySelect(country.name);
break;
}

View File

@ -29,7 +29,7 @@ export function ObservatoryView() {
setLoading(true);
try {
const response = await fetch('/api/news?limit=50');
const response = await fetch('/api/news?limit=100');
if (!response.ok) {
throw new Error('Failed to fetch news');
}
@ -57,21 +57,38 @@ export function ObservatoryView() {
const countries = [
'France', 'USA', 'Canada', 'UK', 'Germany', 'Japan', 'China',
'India', 'Brazil', 'Australia', 'Russia', 'Italy', 'Spain',
'Sudan', 'New York', 'United Nations', 'Ukraine'
'Sudan', 'New York', 'United Nations', 'Ukraine', 'Egypt',
'Mexico', 'South Africa', 'Nigeria', 'Argentina', 'Pakistan',
'Indonesia', 'Saudi Arabia', 'Iran', 'Turkey', 'South Korea',
'Thailand', 'Vietnam', 'Philippines', 'Malaysia', 'Singapore',
'Israel', 'Palestine', 'Syria', 'Iraq', 'Afghanistan',
'Morocco', 'Algeria', 'Tunisia', 'Kenya', 'Ethiopia',
'Greece', 'Poland', 'Sweden', 'Norway', 'Denmark', 'Finland',
'Netherlands', 'Belgium', 'Portugal', 'Switzerland', 'Austria'
];
// Sort countries by length (to prioritize longer names)
const sortedCountries = [...countries].sort((a, b) => b.length - a.length);
const result: Record<string, NewsItem[]> = {};
newsItems.forEach(item => {
countries.forEach(country => {
if (
(item.title && item.title.includes(country)) ||
(item.description && item.description.includes(country))
) {
// For title and description
const titleAndDesc = [
item.title || '',
item.description || ''
].join(' ').toLowerCase();
// Check each country
sortedCountries.forEach(country => {
if (titleAndDesc.includes(country.toLowerCase())) {
if (!result[country]) {
result[country] = [];
}
result[country].push(item);
// Only add once per item
if (!result[country].some(existingItem => existingItem.id === item.id)) {
result[country].push(item);
}
}
});
});