';
return $out;
}
/** A bar with rounded top corners, anchored to the baseline. */
function sp_bar(float $x, float $y, float $w, float $hgt, string $col, float $op): string
{
$r = min(4, $hgt / 2, $w / 2);
$x2 = $x + $w; $y2 = $y + $hgt;
$d = sprintf('M%.1f %.1f L%.1f %.1f L%.1f %.1f Q%.1f %.1f %.1f %.1f L%.1f %.1f Q%.1f %.1f %.1f %.1f Z',
$x, $y2, $x, $y + $r, $x, $y + $r, $x, $y, $x + $r, $y, $x2 - $r, $y, $x2, $y, $x2, $y + $r) . sprintf(' M%.1f %.1f L%.1f %.1f', $x2, $y + $r, $x2, $y2);
// simpler: build the closed shape explicitly
$d = sprintf('M%.1f %.1f L%.1f %.1f Q%.1f %.1f %.1f %.1f L%.1f %.1f Q%.1f %.1f %.1f %.1f L%.1f %.1f Z',
$x, $y2, $x, $y + $r, $x, $y, $x + $r, $y, $x2 - $r, $y, $x2, $y, $x2, $y + $r, $x2, $y2);
return '';
}
/* ------------------------------------------------------------ since the baseline
The sightings register lives on the AI operations platform; its public feed
carries species, date, count, place, the observer's role and a confidence,
nothing personal. Cached here for five minutes. */
const SP_FEED = 'https://districthive.com/ai_discover/api/sightings.php';
function sp_feed(): array
{
$cache = sys_get_temp_dir().'/dh-research-sightings.json';
if (is_file($cache) && filemtime($cache) > time() - 300) {
$j = json_decode((string) file_get_contents($cache), true);
if (is_array($j) && isset($j['sightings'])) { return $j; }
}
$ctx = stream_context_create(['http' => ['timeout' => 4, 'header' => "User-Agent: DistricthiveResearchLabs/1.0\r\n"]]);
$raw = @file_get_contents(SP_FEED, false, $ctx);
$j = $raw !== false ? json_decode($raw, true) : null;
if (is_array($j) && isset($j['sightings'])) { @file_put_contents($cache, $raw); return $j; }
if (is_file($cache)) { $j = json_decode((string) file_get_contents($cache), true); if (is_array($j) && isset($j['sightings'])) { return $j; } }
return ['sightings' => [], 'unavailable' => true, 'baseline_dates' => ['2021-06-04', '2021-07-03', '2021-08-04', '2021-09-02']];
}
function sp_since_baseline(array $species, array $groups, int $thisMonth, array $monthNames): string
{
$feed = sp_feed();
$sightings = array_values(array_filter(is_array($feed['sightings'] ?? null) ? $feed['sightings'] : [],
fn ($x) => is_array($x) && ($x['confidence'] ?? '') !== 'unsure' && is_string($x['date'] ?? null) && preg_match('/^\d{4}-\d\d-\d\d$/', $x['date']) && strtotime($x['date']) !== false && !empty($x['species'])));
$byId = []; // species id => sightings
$new = []; // name => sightings of species not in the baseline
foreach ($sightings as $x) {
if (!empty($x['species_id'])) { $byId[$x['species_id']][] = $x; } else { $new[mb_strtolower((string) $x['species'])][] = $x; }
}
$watch = array_values(array_filter($species, fn ($sp) => $sp['group'] !== 'plant')); // plants are checked by season, not by sighting
$seen = array_values(array_filter($watch, fn ($sp) => !empty($byId[$sp['id']])));
$notYet = array_values(array_filter($watch, fn ($sp) => empty($byId[$sp['id']])));
$plantsSeen = count(array_filter($species, fn ($sp) => $sp['group'] === 'plant' && !empty($byId[$sp['id']])));
$base = $feed['baseline_dates'] ?? [];
$baseLabel = $base !== [] ? date('j M Y', strtotime($base[0])).' to '.date('j M Y', strtotime(end($base))) : '2021';
$out = '
Since the baseline: what has checked out
';
$out .= '
Two comparisons. First, the surveyors\' own count days, eleven visits between 2011 and 2021, set against the period each species is expected here: a ring marks a month in which the species was actually counted on one of those days. Second, every sighting logged since by the team on the operations dashboard, and later by the wildlife scanner, set against the '.count($watch).' birds, fish and invertebrates of the baseline. Plants are checked by season rather than by sighting. Guest reports are not logged, since they cannot be checked.
';
if (!empty($feed['unavailable'])) { $out .= '
The sightings register could not be read just now; the last known state is shown.
'.h(date('j M Y', strtotime($dy['date']))).''.h($dy['what']).''.$n.' species
';
}
$out .= '
Counts on the lagoon were made by telescope from the main road; the 2021 visits also mapped territories on the point, fished the streams and walked the vegetation. Sampling on eleven days cannot show every month, so the rings below are evidence of presence, never of absence.
';
}
/* per species: the baseline calendar with the sightings on it */
$out .= '
Species by species
expected period in colour; a ring where the surveyors counted it on a survey day; a dot where the register has a sighting
'.h(ucfirst($first)).': '.count($xs).' sighting'.(count($xs) === 1 ? '' : 's').', last on '.h(date('j M Y', strtotime($last))).(!empty($xs[0]['place']) ? ', '.h($xs[0]['place']) : '').'
';
}
$out .= '
';
}
$out .= '
expectedbreeding, spawningcounted on a survey dayregister sighting
';
$out .= '
Sightings are logged on the operations dashboard by the team, and later by the wildlife scanner and the acoustic monitors; each carries a date, a count where given, a place and a confidence, never a person. Guest reports are not logged.
Before anything was built, two independent field surveys in 2021 recorded what lives on this land and in the lagoon beside it: the birds that breed on the point, the fish that run up from the fjord, the plants that make up each habitat. This page is that baseline, species by species, with what the surveyors found here, what is known about each one, and the months you are likely to meet it. The Outpost, Districthive\'s Human Recharging Station, now stands on the point; later counts will be compared against these.
'.count($plantsNow).' of the recorded plants are in leaf or flower this month.
';
}
echo '
Months are drawn from each species\' known Icelandic calendar (breeding, passage, wintering, growing season), not from counts made here every month. The survey dates themselves are given on every card.
';
echo '';
/* the year, month by month */
echo '
The year, month by month
';
echo '
How many of the recorded species you can expect in each month, and how many are breeding, spawning or in flower. The pale bar is every species present; the solid bar is the subset in its season. The current month is shaded.
Species pictures are from Wikimedia Commons under their stated licences, or from the survey reports where credited. General facts summarise the published literature on each species; the site facts are the surveyors\' own findings. Corrections are welcome at '.h(CONTACT_EMAIL).'.