ouhenio commited on
Commit
079e609
·
verified ·
1 Parent(s): f7a1251

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +226 -60
app.py CHANGED
@@ -7,29 +7,29 @@ from fastapi import FastAPI
7
  # Create a FastAPI app
8
  app = FastAPI()
9
 
10
- # Sample country data with random progress percentages
11
  def generate_data():
12
  return {
13
- "MX": {"name": "Mexico", "percent": random.randint(10, 90)},
14
- "AR": {"name": "Argentina", "percent": random.randint(10, 90)},
15
- "CO": {"name": "Colombia", "percent": random.randint(10, 90)},
16
- "CL": {"name": "Chile", "percent": random.randint(10, 90)},
17
- "PE": {"name": "Peru", "percent": random.randint(10, 90)},
18
- "ES": {"name": "Spain", "percent": random.randint(10, 90)},
19
- "BR": {"name": "Brazil", "percent": random.randint(10, 90)},
20
- "VE": {"name": "Venezuela", "percent": random.randint(10, 90)},
21
- "EC": {"name": "Ecuador", "percent": random.randint(10, 90)},
22
- "BO": {"name": "Bolivia", "percent": random.randint(10, 90)},
23
- "PY": {"name": "Paraguay", "percent": random.randint(10, 90)},
24
- "UY": {"name": "Uruguay", "percent": random.randint(10, 90)},
25
- "CR": {"name": "Costa Rica", "percent": random.randint(10, 90)},
26
- "PA": {"name": "Panama", "percent": random.randint(10, 90)},
27
- "DO": {"name": "Dominican Republic", "percent": random.randint(10, 90)},
28
- "GT": {"name": "Guatemala", "percent": random.randint(10, 90)},
29
- "HN": {"name": "Honduras", "percent": random.randint(10, 90)},
30
- "SV": {"name": "El Salvador", "percent": random.randint(10, 90)},
31
- "NI": {"name": "Nicaragua", "percent": random.randint(10, 90)},
32
- "CU": {"name": "Cuba", "percent": random.randint(10, 90)}
33
  }
34
 
35
  # HTML template - avoiding f-strings with JavaScript template literals
@@ -237,12 +237,15 @@ HTML_TEMPLATE = """
237
  .then(function(data) {
238
  console.log('GeoJSON data loaded');
239
 
240
- // Filter to only include our target countries
241
  const relevantCountryCodes = Object.keys(countryData);
242
 
243
  // Log countries in data
244
  console.log('Countries in countryData:', relevantCountryCodes);
245
- console.log('First few features from GeoJSON:', data.features.slice(0, 3));
 
 
 
246
 
247
  // Add ocean background
248
  svg.append('rect')
@@ -250,50 +253,213 @@ HTML_TEMPLATE = """
250
  .attr('height', height)
251
  .attr('fill', '#0f1218');
252
 
253
- // Filter the features
254
  const relevantFeatures = data.features.filter(d =>
255
- relevantCountryCodes.includes(d.properties.iso_a2)
256
  );
257
 
258
  console.log('Filtered features count:', relevantFeatures.length);
259
 
260
- // Draw only our target countries
261
- svg.selectAll('.country')
262
- .data(relevantFeatures)
263
- .enter()
264
- .append('path')
265
- .attr('class', 'country')
266
- .attr('d', path)
267
- .attr('fill', function(d) {
268
- const iso = d.properties.iso_a2;
269
- return colorScale(countryData[iso].percent);
270
- })
271
- .attr('stroke', '#0f1218')
272
- .attr('stroke-width', 1)
273
- .on('mouseover', function(event, d) {
274
- const iso = d.properties.iso_a2;
275
-
276
- d3.select(this)
277
- .attr('stroke', '#fff')
278
- .attr('stroke-width', 1.5);
 
 
279
 
280
- tooltip.style('opacity', 1)
281
- .style('left', (event.pageX + 15) + 'px')
282
- .style('top', (event.pageY + 15) + 'px')
283
- .html('<strong>' + countryData[iso].name + '</strong><br/>' +
284
- 'Progress: ' + countryData[iso].percent + '%');
285
- })
286
- .on('mousemove', function(event) {
287
- tooltip.style('left', (event.pageX + 15) + 'px')
288
- .style('top', (event.pageY + 15) + 'px');
289
- })
290
- .on('mouseout', function() {
291
- d3.select(this)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  .attr('stroke', '#0f1218')
293
- .attr('stroke-width', 1);
294
-
295
- tooltip.style('opacity', 0);
296
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
  // Add a legend on the right side of the map
299
  const legendWidth = 200;
 
7
  # Create a FastAPI app
8
  app = FastAPI()
9
 
10
+ # Sample country data with random progress percentages - using the proper ISO codes
11
  def generate_data():
12
  return {
13
+ "MEX": {"name": "Mexico", "percent": random.randint(10, 90)},
14
+ "ARG": {"name": "Argentina", "percent": random.randint(10, 90)},
15
+ "COL": {"name": "Colombia", "percent": random.randint(10, 90)},
16
+ "CHL": {"name": "Chile", "percent": random.randint(10, 90)},
17
+ "PER": {"name": "Peru", "percent": random.randint(10, 90)},
18
+ "ESP": {"name": "Spain", "percent": random.randint(10, 90)},
19
+ "BRA": {"name": "Brazil", "percent": random.randint(10, 90)},
20
+ "VEN": {"name": "Venezuela", "percent": random.randint(10, 90)},
21
+ "ECU": {"name": "Ecuador", "percent": random.randint(10, 90)},
22
+ "BOL": {"name": "Bolivia", "percent": random.randint(10, 90)},
23
+ "PRY": {"name": "Paraguay", "percent": random.randint(10, 90)},
24
+ "URY": {"name": "Uruguay", "percent": random.randint(10, 90)},
25
+ "CRI": {"name": "Costa Rica", "percent": random.randint(10, 90)},
26
+ "PAN": {"name": "Panama", "percent": random.randint(10, 90)},
27
+ "DOM": {"name": "Dominican Republic", "percent": random.randint(10, 90)},
28
+ "GTM": {"name": "Guatemala", "percent": random.randint(10, 90)},
29
+ "HND": {"name": "Honduras", "percent": random.randint(10, 90)},
30
+ "SLV": {"name": "El Salvador", "percent": random.randint(10, 90)},
31
+ "NIC": {"name": "Nicaragua", "percent": random.randint(10, 90)},
32
+ "CUB": {"name": "Cuba", "percent": random.randint(10, 90)}
33
  }
34
 
35
  # HTML template - avoiding f-strings with JavaScript template literals
 
237
  .then(function(data) {
238
  console.log('GeoJSON data loaded');
239
 
240
+ // The relevant country codes
241
  const relevantCountryCodes = Object.keys(countryData);
242
 
243
  // Log countries in data
244
  console.log('Countries in countryData:', relevantCountryCodes);
245
+
246
+ // Log some sample features to check the properties structure
247
+ const sampleFeatures = data.features.slice(0, 3);
248
+ console.log('Sample feature properties:', sampleFeatures.map(f => f.properties));
249
 
250
  // Add ocean background
251
  svg.append('rect')
 
253
  .attr('height', height)
254
  .attr('fill', '#0f1218');
255
 
256
+ // Filter the features - check for id match
257
  const relevantFeatures = data.features.filter(d =>
258
+ relevantCountryCodes.includes(d.id)
259
  );
260
 
261
  console.log('Filtered features count:', relevantFeatures.length);
262
 
263
+ // If we still don't have matches, look more deeply at the data structure
264
+ if (relevantFeatures.length === 0) {
265
+ console.log('No matches found using id, checking all feature properties');
266
+
267
+ // Get the first 10 features to examine their structure
268
+ const firstTen = data.features.slice(0, 10);
269
+ console.log('First ten features:', firstTen);
270
+
271
+ // Look for our countries in all features
272
+ const latinAmericanFeatures = data.features.filter(f => {
273
+ // Check various properties that might contain the country code
274
+ return relevantCountryCodes.includes(f.id) ||
275
+ (f.properties && relevantCountryCodes.includes(f.properties.iso_a3)) ||
276
+ (f.properties && relevantCountryCodes.includes(f.properties.name));
277
+ });
278
+
279
+ console.log('Latin American features found:', latinAmericanFeatures.length);
280
+
281
+ // If there are still no matches, just use all features and filter visually
282
+ if (latinAmericanFeatures.length === 0) {
283
+ console.log('Still no matches, using all features and hiding non-Latin American countries');
284
 
285
+ // Draw all countries but only color our target ones
286
+ svg.selectAll('.country')
287
+ .data(data.features)
288
+ .enter()
289
+ .append('path')
290
+ .attr('class', 'country')
291
+ .attr('d', path)
292
+ .attr('fill', function(d) {
293
+ // Try to match with id or iso_a3
294
+ if (d.id && countryData[d.id]) {
295
+ return colorScale(countryData[d.id].percent);
296
+ } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
297
+ return colorScale(countryData[d.properties.iso_a3].percent);
298
+ } else if (d.properties && d.properties.name && countryData[d.properties.name]) {
299
+ return colorScale(countryData[d.properties.name].percent);
300
+ } else {
301
+ // Try checking if it's a Latin American country by name
302
+ const latinAmericanCountries = [
303
+ 'Mexico', 'Argentina', 'Colombia', 'Chile', 'Peru', 'Spain',
304
+ 'Brazil', 'Venezuela', 'Ecuador', 'Bolivia', 'Paraguay',
305
+ 'Uruguay', 'Costa Rica', 'Panama', 'Dominican Republic',
306
+ 'Guatemala', 'Honduras', 'El Salvador', 'Nicaragua', 'Cuba'
307
+ ];
308
+
309
+ if (d.properties && latinAmericanCountries.includes(d.properties.name)) {
310
+ // Find the matching country in our data
311
+ for (const code in countryData) {
312
+ if (countryData[code].name === d.properties.name) {
313
+ return colorScale(countryData[code].percent);
314
+ }
315
+ }
316
+ }
317
+
318
+ // If not a target country, make it transparent
319
+ return 'transparent';
320
+ }
321
+ })
322
+ .attr('stroke', function(d) {
323
+ // Only show outlines for Latin American countries
324
+ const latinAmericanCountries = [
325
+ 'Mexico', 'Argentina', 'Colombia', 'Chile', 'Peru', 'Spain',
326
+ 'Brazil', 'Venezuela', 'Ecuador', 'Bolivia', 'Paraguay',
327
+ 'Uruguay', 'Costa Rica', 'Panama', 'Dominican Republic',
328
+ 'Guatemala', 'Honduras', 'El Salvador', 'Nicaragua', 'Cuba'
329
+ ];
330
+
331
+ if (d.properties && latinAmericanCountries.includes(d.properties.name)) {
332
+ return '#0f1218';
333
+ } else {
334
+ return 'transparent';
335
+ }
336
+ })
337
+ .attr('stroke-width', 1)
338
+ .on('mouseover', function(event, d) {
339
+ // Only enable hover for target countries
340
+ if (d.id && countryData[d.id]) {
341
+ const iso = d.id;
342
+ showTooltip(event, iso);
343
+ } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
344
+ const iso = d.properties.iso_a3;
345
+ showTooltip(event, iso);
346
+ } else if (d.properties && d.properties.name) {
347
+ // Find the matching country in our data by name
348
+ for (const code in countryData) {
349
+ if (countryData[code].name === d.properties.name) {
350
+ showTooltip(event, code);
351
+ break;
352
+ }
353
+ }
354
+ }
355
+ })
356
+ .on('mousemove', function(event) {
357
+ tooltip.style('left', (event.pageX + 15) + 'px')
358
+ .style('top', (event.pageY + 15) + 'px');
359
+ })
360
+ .on('mouseout', function() {
361
+ d3.select(this)
362
+ .attr('stroke', function(d) {
363
+ const latinAmericanCountries = [
364
+ 'Mexico', 'Argentina', 'Colombia', 'Chile', 'Peru', 'Spain',
365
+ 'Brazil', 'Venezuela', 'Ecuador', 'Bolivia', 'Paraguay',
366
+ 'Uruguay', 'Costa Rica', 'Panama', 'Dominican Republic',
367
+ 'Guatemala', 'Honduras', 'El Salvador', 'Nicaragua', 'Cuba'
368
+ ];
369
+
370
+ if (d.properties && latinAmericanCountries.includes(d.properties.name)) {
371
+ return '#0f1218';
372
+ } else {
373
+ return 'transparent';
374
+ }
375
+ })
376
+ .attr('stroke-width', 1);
377
+
378
+ tooltip.style('opacity', 0);
379
+ });
380
+ } else {
381
+ // Draw only the Latin American features
382
+ svg.selectAll('.country')
383
+ .data(latinAmericanFeatures)
384
+ .enter()
385
+ .append('path')
386
+ .attr('class', 'country')
387
+ .attr('d', path)
388
+ .attr('fill', function(d) {
389
+ if (d.id && countryData[d.id]) {
390
+ return colorScale(countryData[d.id].percent);
391
+ } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
392
+ return colorScale(countryData[d.properties.iso_a3].percent);
393
+ } else {
394
+ return colorScale(50); // Default to mid-range if no match
395
+ }
396
+ })
397
  .attr('stroke', '#0f1218')
398
+ .attr('stroke-width', 1)
399
+ .on('mouseover', function(event, d) {
400
+ if (d.id && countryData[d.id]) {
401
+ const iso = d.id;
402
+ showTooltip(event, iso);
403
+ } else if (d.properties && d.properties.iso_a3 && countryData[d.properties.iso_a3]) {
404
+ const iso = d.properties.iso_a3;
405
+ showTooltip(event, iso);
406
+ }
407
+ })
408
+ .on('mousemove', function(event) {
409
+ tooltip.style('left', (event.pageX + 15) + 'px')
410
+ .style('top', (event.pageY + 15) + 'px');
411
+ })
412
+ .on('mouseout', function() {
413
+ d3.select(this)
414
+ .attr('stroke', '#0f1218')
415
+ .attr('stroke-width', 1);
416
+
417
+ tooltip.style('opacity', 0);
418
+ });
419
+ }
420
+ } else {
421
+ // Draw only our target countries
422
+ svg.selectAll('.country')
423
+ .data(relevantFeatures)
424
+ .enter()
425
+ .append('path')
426
+ .attr('class', 'country')
427
+ .attr('d', path)
428
+ .attr('fill', function(d) {
429
+ const iso = d.id;
430
+ return colorScale(countryData[iso].percent);
431
+ })
432
+ .attr('stroke', '#0f1218')
433
+ .attr('stroke-width', 1)
434
+ .on('mouseover', function(event, d) {
435
+ const iso = d.id;
436
+ showTooltip(event, iso);
437
+ })
438
+ .on('mousemove', function(event) {
439
+ tooltip.style('left', (event.pageX + 15) + 'px')
440
+ .style('top', (event.pageY + 15) + 'px');
441
+ })
442
+ .on('mouseout', function() {
443
+ d3.select(this)
444
+ .attr('stroke', '#0f1218')
445
+ .attr('stroke-width', 1);
446
+
447
+ tooltip.style('opacity', 0);
448
+ });
449
+ }
450
+
451
+ // Function to show tooltip
452
+ function showTooltip(event, iso) {
453
+ d3.select(event.currentTarget)
454
+ .attr('stroke', '#fff')
455
+ .attr('stroke-width', 1.5);
456
+
457
+ tooltip.style('opacity', 1)
458
+ .style('left', (event.pageX + 15) + 'px')
459
+ .style('top', (event.pageY + 15) + 'px')
460
+ .html('<strong>' + countryData[iso].name + '</strong><br/>' +
461
+ 'Progress: ' + countryData[iso].percent + '%');
462
+ }
463
 
464
  // Add a legend on the right side of the map
465
  const legendWidth = 200;