{"id":987618487,"date":"2026-04-01T12:58:01","date_gmt":"2026-04-01T12:58:01","guid":{"rendered":"https:\/\/n2c.gr\/?page_id=987618487"},"modified":"2026-04-06T22:12:13","modified_gmt":"2026-04-06T22:12:13","slug":"birdweather","status":"publish","type":"page","link":"https:\/\/n2c.gr\/en\/birdweather\/","title":{"rendered":"BirdWeather"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The use of the <a href=\"https:\/\/app.birdweather.com\/?_gl=1*prwbpp*_gcl_au*MTE3MTI0ODYzNS4xNzczMjUwNTc4*_ga*MTMzMjQ2Nzg0Ny4xNzYzNzI2MzE5*_ga_TM26CWC1RM*czE3NzU0NjIyMjQkbzYkZzAkdDE3NzU0NjIyMjQkajYwJGwwJGgw\">BirdWeather<\/a> platform represents a new and innovative approach, as it enables the automated detection and identification of birds through calls. By utilizing acoustic field data, continuous recording of species presence becomes possible without the need for direct human observation. Recording stations operate in real time, collecting data that contribute to monitoring bird communities and understanding species presence and activity. At the same time, this automated process enhances the consistency and reliability of observations, providing a modern tool for biodiversity research.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">PUC devices are installed in selected locations. Two devices are located in island ecosystems, one on eastern Naxos (Alazas) and one at the <a href=\"https:\/\/www.gundari.com\/?gad_source=1&amp;gad_campaignid=20808381097&amp;gclid=Cj0KCQjws83OBhD4ARIsACblj1_Lu0h_w9nX8cnsFJV5EVlLS0sTV1plB5T7NsL01oQ8j_fxJQs740gaAiBXEALw_wcB\">Gundari<\/a> resort on Folegandros (Petousis), enabling the recording of avifauna throughout the year. Their placement allows for continuous monitoring of bird migration, as they are situated at strategic points along a migratory corridor in the Aegean. In addition, a third station is installed on a research sailing vessel (Artina) and operates as a mobile monitoring unit, enabling data collection in remote island and marine areas and contributing to a better understanding of species presence and movement patterns.<\/p>\n\n\n\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\" \/>\n\n<div style=\"max-width:1100px;margin:auto;padding:20px;font-family:Arial,sans-serif;\">\n  <h2>BirdWeather Stations<\/h2>\n  <div id=\"status\">Loading...<\/div>\n\n  <div id=\"map\" style=\"height:420px;border:1px solid #ddd;border-radius:12px;margin:20px 0;\"><\/div>\n\n  <div id=\"cards\" style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;margin-bottom:24px;\"><\/div>\n\n  <h3> Detections by Hour Today<\/h3>\n  <canvas id=\"chart\" height=\"100\"><\/canvas>\n\n  <h3 style=\"margin-top:30px;\"> Latest Detections<\/h3>\n  <div id=\"table\"><\/div>\n<\/div>\n\n<script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n\n<script>\nconst stationIds = [\"18538\", \"17794\", \"3743\"];\nlet map, markersLayer, chart;\n\nconst stationDisplayNames = {\n  \"Gundari 2(4) PUC4\":\"Gundari resort - Folegandros\",\n  \"PUC-1\": \"Alazas, Naxos\",\n  \"PUC-3743_Greece\": \"Artina - Research Sailing Vessel Station\"\n};\n\nfunction getDisplayStationName(name) {\n  return stationDisplayNames[name] || name || \"-\";\n}\n\nasync function fetchGraphQL(query, variables) {\n  const res = await fetch(\"https:\/\/app.birdweather.com\/graphql\", {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application\/json\" },\n    body: JSON.stringify({ query, variables })\n  });\n  return res.json();\n}\n\nasync function fetchStations() {\n  const query = `\n    query ($id: ID!) {\n      station(id: $id) {\n        id\n        name\n        location\n        latestDetectionAt\n        coords { lat lon }\n        counts { detections species }\n      }\n    }\n  `;\n  return Promise.all(stationIds.map(id => fetchGraphQL(query, { id })));\n}\n\nasync function fetchDetections() {\n  const query = `\n    query ($stationIds:[ID!], $last:Int) {\n      detections(\n        stationIds:$stationIds,\n        last:$last,\n        confidenceGte:0.8\n      ) {\n        nodes {\n          timestamp\n          confidence\n          score\n          species {\n            commonName\n            scientificName\n          }\n          station {\n            name\n          }\n        }\n      }\n    }\n  `;\n  return fetchGraphQL(query, { stationIds, last: 50 });\n}\n\nfunction formatDate(d) {\n  if (!d) return \"-\";\n  return new Date(d).toLocaleString(\"en-GB\");\n}\n\nfunction isToday(d) {\n  const x = new Date(d);\n  const n = new Date();\n  return x.toDateString() === n.toDateString();\n}\n\nfunction renderTable(data) {\n  if (!data.length) {\n    document.getElementById(\"table\").innerHTML = `\n      <div style=\"padding:14px;border:1px solid #ddd;border-radius:10px;background:#fff;\">\n        No detections available.\n      <\/div>\n    `;\n    return;\n  }\n\n  document.getElementById(\"table\").innerHTML = `\n    <div style=\"\n      max-height: 420px;\n      overflow-y: auto;\n      overflow-x: auto;\n      border: 1px solid #ddd;\n      border-radius: 10px;\n      background: #fff;\n    \">\n      <table style=\"width:100%;min-width:700px;border-collapse:collapse;\">\n        <thead style=\"position:sticky;top:0;background:#f5f5f5;z-index:1;\">\n          <tr>\n            <th style=\"text-align:left;padding:10px;border-bottom:1px solid #ddd;\">Station<\/th>\n            <th style=\"text-align:left;padding:10px;border-bottom:1px solid #ddd;\">Species<\/th>\n            <th style=\"text-align:left;padding:10px;border-bottom:1px solid #ddd;\">Time<\/th>\n            <th style=\"text-align:left;padding:10px;border-bottom:1px solid #ddd;\">Confidence<\/th>\n          <\/tr>\n        <\/thead>\n        <tbody>\n          ${data.map(d => `\n            <tr style=\"border-bottom:1px solid #eee;\">\n              <td style=\"padding:10px;vertical-align:top;\">${getDisplayStationName(d.station?.name)}<\/td>\n              <td style=\"padding:10px;vertical-align:top;\">\n                ${d.species?.commonName || \"-\"}<br>\n                <i>${d.species?.scientificName || \"\"}<\/i>\n              <\/td>\n              <td style=\"padding:10px;vertical-align:top;\">${formatDate(d.timestamp)}<\/td>\n              <td style=\"padding:10px;vertical-align:top;\">${typeof d.confidence === \"number\" ? d.confidence.toFixed(2) : \"-\"}<\/td>\n            <\/tr>\n          `).join(\"\")}\n        <\/tbody>\n      <\/table>\n    <\/div>\n  `;\n}\n\nfunction renderChart(data) {\n  const counts = new Array(24).fill(0);\n\n  data.forEach(d => {\n    const h = new Date(d.timestamp).getHours();\n    counts[h]++;\n  });\n\n  if (chart) chart.destroy();\n\n  chart = new Chart(document.getElementById(\"chart\"), {\n    type: \"bar\",\n    data: {\n      labels: Array.from({ length: 24 }, (_, i) => i + \":00\"),\n      datasets: [{\n        data: counts\n      }]\n    },\n    options: {\n      plugins: {\n        legend: { display: false }\n      },\n      scales: {\n        y: { beginAtZero: true }\n      }\n    }\n  });\n}\n\nfunction renderMap(stations) {\n  if (!map) {\n    map = L.map(\"map\").setView([39, 24], 6);\n    L.tileLayer(\"https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\n      attribution: \"&copy; OpenStreetMap contributors\"\n    }).addTo(map);\n    markersLayer = L.layerGroup().addTo(map);\n  }\n\n  markersLayer.clearLayers();\n\n  const bounds = [];\n\n  stations.forEach(s => {\n    if (!s || !s.coords) return;\n\n    const marker = L.marker([s.coords.lat, s.coords.lon])\n      .bindPopup(`<b>${getDisplayStationName(s.name)}<\/b><br>${s.location || \"-\"}`);\n    markersLayer.addLayer(marker);\n    bounds.push([s.coords.lat, s.coords.lon]);\n  });\n\n  if (bounds.length === 1) {\n    map.setView(bounds[0], 10);\n  } else if (bounds.length > 1) {\n    map.fitBounds(bounds, { padding: [30, 30] });\n  }\n}\n\nfunction renderCards(stations) {\n  document.getElementById(\"cards\").innerHTML =\n    stations.map(s => `\n      <div style=\"border:1px solid #ddd;padding:12px;border-radius:10px;background:#fff;\">\n        <b>${getDisplayStationName(s.name)}<\/b><br>\n        ${s.location || \"-\"}<br>\n        ${formatDate(s.latestDetectionAt)}<br>\n        Detections: ${s.counts?.detections ?? \"-\"}<br>\n        Species: ${s.counts?.species ?? \"-\"}\n      <\/div>\n    `).join(\"\");\n}\n\nasync function load() {\n  document.getElementById(\"status\").innerHTML = \"Loading...\";\n\n  try {\n    const [stRes, detRes] = await Promise.all([\n      fetchStations(),\n      fetchDetections()\n    ]);\n\n    const stations = stRes\n      .map(r => r?.data?.station)\n      .filter(Boolean);\n\n    let detections = detRes?.data?.detections?.nodes || [];\n\n    detections = detections\n      .filter(d => isToday(d.timestamp))\n      .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));\n\n    document.getElementById(\"status\").innerHTML =\n      `\u2714 ${stations.length} stations \u2022 ${detections.length} detections today`;\n\n    renderCards(stations);\n    renderMap(stations);\n    renderChart(detections);\n    renderTable(detections);\n  } catch (err) {\n    console.error(err);\n    document.getElementById(\"status\").innerHTML = \"Error loading data.\";\n  }\n}\n\nload();\nsetInterval(load, 30000);\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The use of the BirdWeather platform represents a new and innovative approach, as it enables the automated detection and identification of birds through calls. By utilizing acoustic field data, continuous recording of species presence becomes possible without the need for direct human observation. Recording stations operate in real time, collecting data that contribute to monitoring [&hellip;]<\/p>\n","protected":false},"author":8,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-987618487","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/pages\/987618487","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/users\/8"}],"replies":[{"embeddable":true,"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/comments?post=987618487"}],"version-history":[{"count":44,"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/pages\/987618487\/revisions"}],"predecessor-version":[{"id":987618612,"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/pages\/987618487\/revisions\/987618612"}],"wp:attachment":[{"href":"https:\/\/n2c.gr\/en\/wp-json\/wp\/v2\/media?parent=987618487"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}