{"id":4701,"date":"2025-09-01T20:33:34","date_gmt":"2025-09-01T20:33:34","guid":{"rendered":"https:\/\/paragliding-in-madeira.com\/weather\/?page_id=4701"},"modified":"2026-04-21T15:18:38","modified_gmt":"2026-04-21T14:18:38","slug":"madeira-roads-live","status":"publish","type":"page","link":"https:\/\/paragliding-in-madeira.com\/weather\/madeira-roads-live\/","title":{"rendered":"Madeira Roads"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Roads-banner-gpt-ext-1024x576.webp\" alt=\"Roads banner gpt ext\" class=\"wp-image-5169\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Roads-banner-gpt-ext-1024x576.webp 1024w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Roads-banner-gpt-ext-300x169.webp 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Roads-banner-gpt-ext-768x432.webp 768w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Roads-banner-gpt-ext-1536x864.webp 1536w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Roads-banner-gpt-ext.webp 1820w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-a89b3969 wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/\">Home<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/weather-info\/\">Weather info<\/a><\/div>\n<\/div>\n\n\n\n<p class=\"has-text-align-center\"><a href=\"https:\/\/paragliding-in-madeira.com\/weather\/webcams\/\" title=\"\">Live<\/a> | <a href=\"https:\/\/paragliding-in-madeira.com\/weather\/all-cams\/\" title=\"\">Pictures<\/a> | <strong>Roads<\/strong><\/p>\n\n\n\n<div style=\"height:8px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"has-text-align-center\"><a style=\"text-decoration: none;\" href=\"https:\/\/cams.milhofrito.uk\" target=\"_blank\" rel=\"noopener\" title=\"\">Check the real time status of traffic<\/a><br>Powered by <a href=\"https:\/\/www.vialitoral.com\/map\" target=\"_blank\" rel=\"noreferrer noopener\">ViaLitoral<\/a><br>Currently working to establish a connection to show the live images here<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-default\"\/>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div id=\"traffic-cam-container\">\n  <style>\n    \/* Container *\/\n    #traffic-cam-container {\n      max-width: 1344px;\n      margin: 0 auto;\n      padding: 1rem;\n      font-family: 'Inter', sans-serif;\n      background: transparent;\n    }\n    @media (min-width: 640px) { #traffic-cam-container { padding: 2rem; } }\n\n    \/* Header *\/\n    #traffic-cam-container .header { text-align: center; margin-bottom: 2rem; }\n    #traffic-cam-container .header .title { font-size: 1.7rem; font-weight: 600; color: #1f2937; margin-bottom: 0.4rem; }\n    #traffic-cam-container .header .subtitle { font-size: 1.125rem; color: #4b5563; margin-bottom: 1rem; }\n    \n    \/* Search Input and Filters Container *\/\n    #traffic-cam-container .search-and-filters {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      gap: 1rem;\n    }\n    \n    #traffic-cam-container .header .filter-input {\n      padding: 0.5rem 1rem;\n      width: 100%;\n      max-width: 32rem; \/* The new fix: this prevents stretching *\/\n      border: 1px solid #d1d5db;\n      border-radius: 0.25rem;\n    }\n\n    \/* Location Filter Buttons *\/\n    #traffic-cam-container .location-filters {\n      display: flex;\n      justify-content: center;\n      flex-wrap: wrap;\n      gap: 0.5rem;\n    }\n    #traffic-cam-container .location-filters button {\n      background-color: #f3f4f6;\n      color: #374151;\n      border: 1px solid #d1d5db;\n      padding: 0.5rem 1rem;\n      border-radius: 9999px; \/* Full rounded corners *\/\n      cursor: pointer;\n      transition: background-color 0.2s, color 0.2s, transform 0.2s;\n      font-weight: 500;\n    }\n    #traffic-cam-container .location-filters button:hover {\n      background-color: #e5e7eb;\n      transform: translateY(-2px);\n    }\n    #traffic-cam-container .location-filters button.active {\n      background-color: #4f46e5;\n      color: white;\n      border-color: #4f46e5;\n      transform: translateY(-2px);\n    }\n\n    \/* Grid *\/\n    #traffic-cam-container #camera-grid {\n      display: grid;\n      grid-template-columns: repeat(1, 1fr);\n      gap: 1.5rem;\n    }\n    @media (min-width: 640px) { #traffic-cam-container #camera-grid { grid-template-columns: repeat(2, 1fr); } }\n    @media (min-width: 1024px) { #traffic-cam-container #camera-grid { grid-template-columns: repeat(3, 1fr); } }\n\n    \/* Camera item *\/\n    #traffic-cam-container .camera-item {\n      position: relative;\n      border-radius: 6px;\n      box-shadow: 0 4px 6px rgba(0,0,0,0.1);\n      transition: transform .2s;\n      cursor: default;\n      background: transparent;\n      overflow: visible; \/* important for date\/time visibility *\/\n    }\n    #traffic-cam-container .camera-item:hover { transform: translateY(-4px); }\n\n    #traffic-cam-container .camera-image {\n      width: 100%;\n      display: block;\n      min-height: 180px;\n      border-radius: 6px;\n      object-fit: contain; \/* shows full image including top-right date *\/\n    }\n\n    #traffic-cam-container .camera-title {\n      padding: .5rem .75rem;\n      text-align: center;\n      font-weight: 500;\n      color: white;\n      background-color: #4f46e5;\n      border-radius: 0 0 6px 6px;\n      margin-top: -3px;\n    }\n\n    #traffic-cam-container .refresh-btn {\n      display: block;\n      margin: 0.5rem auto;\n      font-size: 0.8rem;\n      padding: 2px 6px;\n      background: #e5e7eb;\n      color: #374151;\n      border-radius: 4px;\n      cursor: pointer;\n      transition: background 0.2s;\n    }\n    #traffic-cam-container .refresh-btn:hover { background: #d1d5db; }\n\n    #traffic-cam-container .fade-in { animation: fadeIn .5s ease-in forwards; }\n    @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }\n  <\/style>\n\n  <header class=\"header\">\n    <h1 class=\"title\">Live Cams<\/h1>\n    <p class=\"subtitle\">From Via Litoral (VR1)<\/p>\n    <div class=\"search-and-filters\">\n        <input id=\"filterInput\" type=\"text\" placeholder=\"Filter by camera title...\" class=\"filter-input\" \/>\n        <div class=\"location-filters\">\n            <button id=\"all-btn\" class=\"active\">All<\/button>\n            <button id=\"ribeira-brava-btn\">Rib. Brava<\/button>\n            <button id=\"cam-lobos-btn\">Cam Lobos<\/button>\n            <button id=\"funchal-btn\">Funchal<\/button>\n            <button id=\"santa-cruz-btn\">Santa Cruz<\/button>\n            <button id=\"machico-btn\">Machico<\/button>\n        <\/div>\n    <\/div>\n  <\/header>\n\n  <div id=\"camera-grid\"><\/div>\n\n  <script>\n    \/\/ Camera Titles (full 156)\n    const cameraTitles = { \n      '001': 'Ribeira Brava',\n  '002': 'T\u00fanel da Ribeira Brava (Sul)',\n  '003': 'Camera 3',\n  '004': 'Camera 4',\n  '005': 'T\u00fanel da Ribeira Brava (Norte)',\n  '006': 'Camera 6',\n  '007': 'Camera 7',\n  '008': 'R.B. Ponte Ribeira dos Mel\u00f5es',\n  '009': 'R.B. Viaduto da Amoreira',\n  '010': 'Camera 10',\n  '011': 'N\u00f3 do Campan\u00e1rio',\n  '012': 'Camera 12',\n  '013': 'Camera 13',\n  '014': 'Ponte do Campan\u00e1rio',\n  '015': 'Camera 15',\n  '016': 'Camera 16',\n  '017': 'Camera 17',\n  '018': 'T\u00fanel da Quinta Grande (Sul)',\n  '019': 'Camera 19',\n  '020': 'T\u00fanel da Quinta Grande (Norte)',\n  '021': 'Camera 21',\n  '022': 'N\u00f3 da Quinta Grande',\n  '023': 'Camera 23',\n  '024': 'T\u00fanel do Cabo Gir\u00e3o (Sul)',\n  '025': 'Camera 25',\n  '026': 'T\u00fanel do Cabo Gir\u00e3o (Norte)',\n  '027': 'Camera 27',\n  '028': 'C.L. Ponte da Caldeira',\n  '029': 'Camera 29',\n  '030': 'Camera 30',\n  '031': 'Camera 31',\n  '032': 'C.L. T\u00fanel da Alforra (Norte)',\n  '033': 'Camera 33',\n  '034': 'C.L. Ponte do Vig\u00e1rio',\n  '035': 'Camera 35',\n  '036': 'C.L. Ponte dos Frades (P.Pe\u00f5es)',\n  '037': 'C.L. N\u00f3 da Ponte dos Frades',\n  '038': 'Camera 38',\n  '039': 'N\u00f3 de C\u00e2mara de Lobos',\n  '040': 'Camera 40',\n  '041': 'Camera 41',\n  '042': 'S.M. N\u00f3 das Quebradas',\n  '043': 'Camera 43',\n  '044': 'Camera 44',\n  '045': 'S.M. Mercado Abastecedor',\n  '046': 'S.M. N\u00f3 do Esmeraldo',\n  '047': 'Camera 47',\n  '048': 'Camera 48',\n  '049': 'S.M. Sa\u00edda Oeste - Dr Barreto',\n  '050': 'Cruz de Carvalho',\n  '051': 'Camera 51',\n  '052': 'S.M. Sa\u00edda Oeste - Pilar',\n  '053': 'Funchal Ponte do Ribeiro Seco',\n  '054': 'Funchal Santo Ant\u00f3nio',\n  '055': 'Camera 55',\n  '056': 'Camera 56',\n  '057': 'T\u00fanel de Jo\u00e3o Abel de Freitas (Sul)',\n  '058': 'T\u00fanel de Jo\u00e3o Abel de Freitas (Norte)',\n  '059': 'N\u00f3 de Santa Luzia',\n  '060': 'T\u00fanel dos Marmeleiros (Sul)',\n  '061': 'Camera 61',\n  '062': 'T\u00fanel da Quinta da Palmeira (Sul)',\n  '063': 'T\u00fanel da Quinta da Palmeira (Norte)',\n  '064': 'Viaduto do Comboio',\n  '065': 'N\u00f3 de Pestana J\u00fanior',\n  '066': 'Camera 66',\n  '067': 'Camera 67',\n  '068': 'Ponte Jo\u00e3o Gomes',\n  '069': 'Camera 69',\n  '070': 'Jardim Bot\u00e2nico',\n  '071': 'Camera 71',\n  '072': 'Camera 72',\n  '073': 'N\u00f3 da Boa Nova',\n  '074': 'Canto do Muro',\n  '075': 'Neves',\n  '076': 'T\u00fanel de Jo\u00e3o Abel de Freitas (Norte)',\n  '077': 'Camera 77',\n  '078': 'Camera 78',\n  '079': 'N\u00f3 do Pinheiro Grande',\n  '080': 'Camera 80',\n  '081': 'Camera 81',\n  '082': 'N\u00f3 da Cancela',\n  '083': 'Camera 83',\n  '084': 'Camera 84',\n  '085': 'T\u00fanel da Abegoaria Oeste (Norte)',\n  '086': 'T\u00fanel da Abegoaria Este (Sul)',\n  '087': 'Camera 87',\n  '088': 'Abegoaria Este',\n  '089': 'Camera 89',\n  '090': 'Cani\u00e7o (Estrada do Garajau)',\n  '091': 'N\u00f3 do Cani\u00e7o',\n  '092': 'Camera 92',\n  '093': 'Ponte do Cani\u00e7o',\n  '094': 'Camera 94',\n  '095': 'Ponte dos Reis Magos',\n  '096': 'Camera 96',\n  '097': 'T\u00fanel da M\u00e3e de Deus (Sul)',\n  '098': 'Camera 98',\n  '099': 'Camera 99',\n  '100': 'N\u00f3 do Porto Novo',\n  '101': 'Ponte do Porto Novo',\n  '102': 'Camera 102',\n  '103': 'N\u00f3 de Gaula',\n  '104': 'Gaula',\n  '105': 'Camera 105',\n  '106': 'Gaula (Este)',\n  '107': 'Camera 107',\n  '108': 'N\u00f3 da Boaventura - Gaula',\n  '109': 'N\u00f3 de S\u00e3o Pedro',\n  '110': 'Camera 110',\n  '111': 'Ponte de Santa Cruz',\n  '112': 'Camera 112',\n  '113': 'T\u00fanel de Santa Catarina (Norte)',\n  '114': 'N\u00f3 de Santa Cruz',\n  '115': 'Camera 115',\n  '116': 'Aeroporto (T.Carga)',\n  '117': 'Camera 117',\n  '118': 'N\u00f3 do Aeroporto',\n  '119': 'Camera 119',\n  '120': 'N\u00f3 do Aeroporto Leste',\n  '121': 'Viaduto do Seixo',\n  '122': 'Camera 122',\n  '123': 'N\u00f3 de \u00c1gua de Pena',\n  '124': 'Camera 124',\n  '125': 'T\u00fanel da Queimada (Sul)',\n  '126': 'Camera 126',\n  '127': 'Camera 127',\n  '128': 'N\u00f3 de Machico Sul',\n  '129': 'Torre - Machico',\n  '130': 'Camera 128',\n  '131': 'Piquinho',\n  '132': 'Camera 132',\n  '133': 'Camera 133',\n  '134': 'Camera 134',\n  '135': 'N\u00f3 de Machico (Norte)',\n  '136': 'Camera 136',\n  '137': 'T\u00fanel da Fazenda (Norte)',\n  '138': 'Camera 138',\n  '139': 'Camera 139',\n  '140': 'Camera 140',\n  '141': 'Camera 141',\n  '142': 'Camera 142',\n  '143': 'Camera 143',\n  '144': 'Camera 144',\n  '145': 'Camera 145',\n  '146': 'N\u00f3 do Cani\u00e7al',\n  '147': 'Camera 146',\n  '148': 'Camera 147',\n  '149': 'Ponte entre \u00c1guas',\n  '150': 'Camera 150',\n  '151': 'Camera 151',\n  '152': 'Camera 152',\n  '153': 'Camera 153',\n  '154': 'Camera 154',\n  '155': 'Camera 155',\n  '156': 'Rotunda Porto Cani\u00e7al'\n      \/\/ ...all 156 cameras\n    };\n\n    \/\/ Location-based camera groupings\n    const locationFilters = {\n      'ribeira-brava': ['001', '002', '003', '004', '005', '006', '007', '008', '009', '010'],\n      'cam-lobos': ['011', '012', '013', '014', '015', '016', '017', '018', '019', '020', '021', '022', '023', '024', '025', '026', '027', '028', '029', '030', '031', '032', '033', '034', '035', '036', '037', '038', '039', '040', '041'],\n      'funchal': ['042', '043', '044', '045', '046', '047', '048', '049', '050', '051', '052', '053', '054', '055', '056', '057', '058', '059', '060', '061', '062', '063', '064', '065', '066', '067', '068', '069', '070', '071', '072', '073', '074', '075', '076', '077', '078', '079', '080', '081', '082', '083', '084', '085', '086', '087', '088', '089', '090'],\n      'santa-cruz': ['091', '092', '093', '094', '095', '096', '097', '098', '099', '100', '101', '102', '103', '104', '105', '106', '107', '108', '109', '110', '111', '112', '113', '114', '115', '116', '117', '118', '119', '120', '121', '122', '123', '124'],\n      'machico': ['125', '126', '127', '128', '129', '130', '131', '132', '133', '134', '135', '136', '137', '138', '139', '140', '141', '142', '143', '144', '145', '146', '147', '148', '149', '150', '151', '152', '153', '154', '155', '156']\n    };\n\n    \/\/ Cameras array\n    const cameras = Array.from({ length: 156 }, (_, i) => {\n      const id = String(i+1).padStart(3,'0');\n      return { id, title: cameraTitles[id] || `VR1 ${id}` };\n    });\n\n    \/\/ Create camera HTML\n    function createCameraElement(camera) {\n      const article = document.createElement('article');\n      article.className = 'camera-item';\n      article.dataset.id = camera.id;\n      article.dataset.title = camera.title.toLowerCase();\n      article.innerHTML = `\n        <img class=\"camera-image\" id=\"img-${camera.id}\" loading=\"lazy\" decoding=\"async\" alt=\"${camera.title}\">\n        <div class=\"camera-title\">${camera.title}<\/div>\n        <button class=\"refresh-btn\">Refresh<\/button>\n      `;\n      article.querySelector('.refresh-btn').addEventListener('click', () => {\n        const img = document.getElementById(`img-${camera.id}`);\n        img.src = `https:\/\/www.vialitoral.com\/cctv.php?image=${camera.id}&t=${Date.now()}`;\n      });\n      return article;\n    }\n\n    \/\/ Load cameras\n    function loadCamera(camera) {\n      const img = document.getElementById(`img-${camera.id}`);\n      img.onload = () => img.closest('.camera-item')?.classList.add('fade-in');\n      img.onerror = () => img.closest('.camera-item')?.remove();\n      \/\/ The browser's native lazy loading handles initial image display.\n      \/\/ We only need to set the src once and the browser will handle the rest.\n      img.src = `https:\/\/www.vialitoral.com\/cctv.php?image=${camera.id}`;\n    }\n\n    \/\/ Filter cameras based on an array of IDs\n    function filterCameras(idsToShow) {\n        document.querySelectorAll('.camera-item').forEach(item => {\n            if (idsToShow === 'all' || idsToShow.includes(item.dataset.id)) {\n                item.style.display = '';\n            } else {\n                item.style.display = 'none';\n            }\n        });\n    }\n\n    \/\/ Init\ndocument.addEventListener('DOMContentLoaded', () => {\n    const grid = document.getElementById('camera-grid');\n    const filterInput = document.getElementById('filterInput');\n    const locationButtons = document.querySelectorAll('.location-filters button');\n\n    \/\/ Create camera elements\n    cameras.forEach(cam => {\n        \/\/ Skip No named Cameras\n        if (cam.title.startsWith('Camera')) return; \n\n        \/\/ 1. Create and add the element\n        grid.appendChild(createCameraElement(cam));\n        \n        \/\/ 2. Load the image immediately after adding the element\n        loadCamera(cam);\n    });\n\n    \/\/ removed the old 'cameras.forEach(loadCamera);' from here\n\n      \/\/ Filter\/search input\n      filterInput.addEventListener('input', e => {\n        const q = e.target.value.toLowerCase();\n        locationButtons.forEach(btn => btn.classList.remove('active'));\n        if (q === '') {\n          document.getElementById('all-btn').classList.add('active');\n        }\n        document.querySelectorAll('.camera-item').forEach(item => {\n          item.style.display = item.dataset.title.includes(q) ? '' : 'none';\n        });\n      });\n\n      \/\/ Location filter buttons\n      locationButtons.forEach(button => {\n        button.addEventListener('click', () => {\n          filterInput.value = ''; \/\/ Clear search\n          locationButtons.forEach(btn => btn.classList.remove('active'));\n          button.classList.add('active');\n\n          const locationId = button.id.replace('-btn', '');\n          if (locationId === 'all') {\n            filterCameras('all');\n          } else {\n            filterCameras(locationFilters[locationId]);\n          }\n        });\n      });\n    });\n  <\/script>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Live | Pictures | Roads Check the real time status of trafficPowered by ViaLitoralCurrently working to establish a connection to show the live images here Live Cams From Via Litoral (VR1) All Rib. Brava Cam Lobos Funchal Santa Cruz Machico<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"slim_seo":{"title":"Madeira Roads live","description":"Live images on the freeway to check the real time status of traffic. Powered by ViaLitoral"},"footnotes":""},"class_list":["post-4701","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/4701","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/comments?post=4701"}],"version-history":[{"count":86,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/4701\/revisions"}],"predecessor-version":[{"id":10701,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/4701\/revisions\/10701"}],"wp:attachment":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/media?parent=4701"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}