Advertisement
lawre

Untitled

Feb 8th, 2025
42
0
6 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 60.09 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html lang="it">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Mappa Municipio X - Biblioteca Elsa Morante</title>
  7.     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" />
  8.     <link rel="stylesheet" href="https://unpkg.com/leaflet-routing-machine@3.2.12/dist/leaflet-routing-machine.css" />
  9.     <link rel="icon" href="favicon.png" type="image/png">
  10.     <style>
  11.  
  12.         :root {
  13.     --primary: #bc0b36; /* Colore primario */
  14.      }
  15.  
  16.        
  17. body, html {
  18.     margin: 0;
  19.     padding: 0;
  20.     height: 100%;
  21.     width: 100%;
  22.     overflow: hidden;  /* Prevents scrolling */
  23.     position: fixed;   /* Ensures no bouncing/scrolling on mobile */
  24. }
  25.  
  26. #map {
  27.     height: 100%;     /* Changed from 100vh to 100% */
  28.     width: 100%;
  29.     position: absolute;
  30.     top: 0;
  31.     left: 0;
  32. }
  33. .search-box{position:absolute;top:10px;left:50px;z-index:1000;background:#fff;padding:10px;border-radius:5px;box-shadow:0 0 15px rgba(0,0,0,.2);display:flex;align-items:center}
  34. .search-box input{width:250px;margin-right:10px;padding:5px 10px;border:1px solid #ccc;border-radius:3px;font-size:14px}
  35. .search-box button {
  36.     background-color: var(--primary);
  37.     color: #fff;
  38.     border: none;
  39.     padding: 5px 10px;
  40.     border-radius: 3px;
  41.     cursor: pointer;
  42.     font-size: 14px;
  43. }
  44. .search-box .icon{width:24px;height:24px;margin-right:10px;cursor:pointer}
  45. .search-box .separator{width:1px;height:24px;background-color:#ccc;margin:0 10px}
  46.  
  47.        
  48. /* Barra laterale fissa */
  49. .sidebar {
  50.     position: absolute;
  51.     top: 0;
  52.     left: 0;
  53.     height: 100%;
  54.     width: 60px;
  55.     background: rgba(255, 255, 255, 0.9);
  56.     box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
  57.     z-index: 1000;
  58.     display: flex;
  59.     flex-direction: column;
  60.     align-items: center;
  61.     padding-top: 40px; /* Riduci questo valore (era 20px) */
  62. }
  63.  
  64. .sidebar button {
  65.     background: transparent;
  66.     border: none;
  67.     cursor: pointer;
  68.     margin-bottom: 20px;
  69.     margin-top: 5px;
  70.     position: relative;
  71.     transition: background-color 0.3s ease;
  72. }
  73.  
  74. /* Effetto hover sui pulsanti */
  75. .sidebar button:hover {
  76.     background-color: rgba(0, 0, 0, 0.1); /* Cambia colore di sfondo al passaggio del mouse */
  77.     border-radius: 5px; /* Arrotonda gli angoli */
  78. }
  79.  
  80. /* Add new styles for active button state */
  81. .sidebar button.active {
  82.     background-color: rgba(0,0,0,0.1);
  83.     border-radius: 5px;
  84. }
  85.  
  86.  
  87.        
  88. /* Stile per il tooltip */
  89. .sidebar button::after {
  90.     content: attr(data-tooltip); /* Usa l'attributo data-tooltip per il testo */
  91.     position: absolute;
  92.     left: 60px; /* Posizione a destra del pulsante */
  93.     top: 50%;
  94.     transform: translateY(-50%);
  95.     background-color: rgba(0, 0, 0, 0.8);
  96.     color: #fff;
  97.     padding: 5px 10px;
  98.     border-radius: 4px;
  99.     font-size: 12px;
  100.     white-space: nowrap;
  101.     opacity: 0;
  102.     visibility: hidden;
  103.     transition: opacity 0.3s ease, visibility 0.3s ease;
  104. }
  105.  
  106. /* Mostra il tooltip al passaggio del mouse */
  107. .sidebar button:hover::after {
  108.     opacity: 1;
  109.     visibility: visible;
  110. }
  111.  
  112.  
  113.        
  114.   .sidebar button img {
  115.     width: 24px;
  116.     height: 24px;
  117.     padding: 0;
  118.     margin: 0;
  119. }
  120.  
  121.         /* Seconda barra laterale (espansione) */
  122.     .sidebar-expanded {
  123.     overflow-y: auto;
  124.     position: absolute;
  125.     top: 0;
  126.     left: 60px;
  127.     height: 100%;
  128.     width: 0;
  129.     background: #f4f7f0;
  130.     box-shadow: 2px 0 10px rgba(0,0,0,0.1);
  131.     z-index: 999;
  132.     transition: width 0.3s ease;
  133. }
  134.  
  135.        
  136. .sidebar-expanded.open {
  137.     width: 400px;
  138.     padding: 20px;
  139.     box-sizing: border-box;
  140. }
  141.  
  142.  
  143. .sidebar-close-icon {
  144.     position: absolute;
  145.     right: 8px;
  146.     top: 8px;
  147.     cursor: pointer;
  148.     opacity: 0.8;
  149.     transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  150.     z-index: 1000;
  151.     width: 28px;
  152.     height: 28px;
  153.     display: flex;
  154.     align-items: center;
  155.     justify-content: center;
  156.     border-radius: 50%;
  157.     background: url('x.svg') center center no-repeat;
  158.     background-size: 14px;
  159. }
  160.  
  161. .sidebar-close-icon:hover {
  162.     opacity: 1;
  163.     background-color: rgba(0, 0, 0, 0.05);
  164.     transform: scale(1.08);
  165.     box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
  166. }
  167.  
  168.  
  169.  
  170.        
  171.         /* Casella di ricerca */
  172.         /* Stile per il pannello di ricerca */
  173. .search-container {
  174.     position: relative; /* Permette di posizionare il riquadro dei risultati relativamente a questo contenitore */
  175.     padding: 20px;
  176.     display: none;
  177. }
  178.         .sidebar-expanded.open .search-container {
  179.             display: block; /* Mostra la casella di ricerca */
  180.         }
  181.         .search-container input {
  182.             width: 100%;
  183.             padding: 8px;
  184.             border: 1px solid #4285f4; /* Contorno blu */
  185.             border-radius: 4px; /* Bordi arrotondati */
  186.             font-size: 14px;
  187.             outline: none; /* Rimuove il bordo predefinito */
  188.         }
  189.         .search-container button {
  190.             width: 100%;
  191.             padding: 8px;
  192.             margin-top: 10px;
  193.             background-color: var(--primary);
  194.             color: #fff;
  195.             border: none;
  196.             border-radius: 4px;
  197.             cursor: pointer;
  198.             font-size: 14px;
  199.         }
  200.  
  201. .search-container .icon {
  202.     width: 24px;
  203.     height: 24px;
  204.     padding: 6px;
  205.     border-radius: 4px;
  206.     transition: background-color 0.2s ease;
  207. }
  208.  
  209. .search-container .icon:hover {
  210.     background-color: rgba(0, 0, 0, 0.05);
  211. }
  212.        
  213. .legend.visible {
  214.     right: 40px; /* Modificato da 0 a 40px per lasciare spazio alla maniglia */
  215. }
  216.  
  217.  
  218. .legend-container {
  219.     padding: 20px;
  220. }
  221.  
  222. .legend-container h4 {
  223.     margin: 0 0 12px 0;
  224.     font-size: 14px;
  225.     color: #333;
  226. }
  227.  
  228. /* Rimuovere questi stili dalla classe .legend esistente poiché non serviranno più */
  229. .legend {
  230.     display: none;
  231. }
  232.        
  233. /* Assicurati che il contenitore della legenda abbia una posizione relativa */
  234. .legend {
  235.     position: fixed; /* Fisso in modo che resti visibile anche durante lo scroll */
  236.     top: 10px; /* Vicino al bordo superiore */
  237.     bottom: 25px; /* Vicino al bordo inferiore */
  238.     right: -350px; /* Posizione iniziale fuori dalla vista */
  239.     z-index: 1000;
  240.     width: 250px; /* Larghezza della legenda */
  241.     background: rgba(255,255,255,.95);
  242.     padding: 15px 20px;
  243.     font: 13px/15px 'Segoe UI', system-ui, sans-serif;
  244.     box-shadow: 0 5px 25px rgba(0,0,0,.15);
  245.     border-radius: 12px;
  246.     border: 1px solid rgba(0,0,0,.1);
  247.     backdrop-filter: blur(4px);
  248.     transition: right .3s ease;
  249.     overflow: visible;
  250. }
  251.  
  252. .legend h4 {
  253.     margin: 0 0 12px 0;  /* Margin più bilanciato */
  254.     font-size: 14px;
  255. }
  256.  
  257. .legend-grid {
  258.     display: grid;
  259.     grid-template-columns: 1fr;
  260.     gap: 6px;           /* Gap intermedio tra gli elementi */
  261.     margin-top: 20px; /* Aggiungi un margine superiore per abbassare i municipi */
  262. }
  263.  
  264.        
  265. /* Stili comuni */
  266. .legend-item {
  267.     display: flex;
  268.     padding: 6px 10px;
  269.     border-radius: 6px;
  270.     cursor: pointer;
  271.     transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  272.     background: rgba(255, 255, 255, 0.7);
  273.     border: 1px solid transparent;
  274.     position: relative;
  275.     padding-right: 40px;
  276. }
  277.  
  278. /* Stili specifici per .legend-item (default) */
  279. .legend-item {
  280.     align-items: center;
  281.     flex-direction: column;
  282. }
  283.  
  284. /* Stili specifici per .federate-container .legend-item */
  285. .federate-container .legend-item {
  286.     align-items: flex-start;
  287.     flex-direction: row; /* Imposta esplicitamente il valore predefinito */
  288. }        
  289.  
  290. .legend-item span {
  291.     width: 18px;         /* Dimensione icona bilanciata */
  292.     height: 18px;
  293.     margin-right: 10px;  /* Margin bilanciato */
  294.     border-radius: 4px;
  295.     box-shadow: 0 2px 6px rgba(0,0,0,0.1);
  296. }
  297.        
  298.  
  299. .legend-item:hover {
  300.     background: rgba(241, 242, 246,0.6);
  301.     transform: translateX(3px);
  302.     border-color: #dfe4ea;
  303. }
  304.  
  305. .legend-item.highlighted {
  306.     background: #f8f9fa;
  307.     box-shadow: 0 3px 15px rgba(0,0,0,0.1);
  308.     border-color: #ced6e0;
  309. }
  310.  
  311.  
  312.  
  313. .legend-item:hover span {
  314.     transform: scale(1.1);
  315. }
  316.  
  317. .legend-item .text {
  318.     font-size: 13px;
  319.     color: #4a5568;
  320.     font-weight: 500;
  321.     letter-spacing: 0.02em;
  322.     margin-right: 20px;
  323. }
  324.  
  325.   .legend-item.disabled {
  326.             opacity: 0.5;
  327.             pointer-events: none;
  328.         }
  329.  
  330.  
  331.  
  332. .legend-close-icon {
  333.     position: absolute;
  334.     right: 8px;
  335.     top: 8px;
  336.     cursor: pointer;
  337.     opacity: .8;
  338.     transition: all .2s cubic-bezier(.4, 0, .2, 1);
  339.     z-index: 1000;
  340.     width: 28px;  /* Ridotto da 32px a 28px */
  341.     height: 28px; /* Ridotto da 32px a 28px */
  342.     display: flex;
  343.     align-items: center;
  344.     justify-content: center;
  345.     border-radius: 50%;
  346.     background: url('x.svg') center center no-repeat;
  347.     background-size: 14px; /* Ridotto da 16px a 14px per mantenere le proporzioni */
  348. }
  349.  
  350. .legend-close-icon:hover {
  351.     opacity: 1;
  352.     background-color: rgba(0, 0, 0, 0.05); /* Cambiato da rgba(255,68,68,.15) a un grigio più sottile */
  353.     transform: scale(1.08); /* Ridotto da 1.15 a 1.08 per un effetto più sottile */
  354.     box-shadow: 0 1px 4px rgba(0,0,0,0.05); /* Ridotto l'effetto ombra */
  355. }
  356.        
  357.  
  358.        
  359.  
  360.  
  361.        
  362.        
  363. .municipio-highlight {
  364.     stroke-width: 4px !important;
  365.     stroke-opacity: 0.9 !important;
  366.     filter: drop-shadow(0 0 12px rgba(0,0,0,0.15));
  367.     animation: pulseBorder 1.5s ease-in-out infinite;
  368. }
  369.  
  370. .municipio-selected {
  371.     stroke-width: 5px !important;
  372.     stroke-opacity: 1 !important;
  373.     fill-opacity: 0.2 !important;
  374.     filter: drop-shadow(0 0 15px rgba(0, 0, 0, 0.3));
  375. }
  376.  
  377. @keyframes pulseBorder {
  378.     0% { stroke-width: 4px; }
  379.     50% { stroke-width: 5px; }
  380.     100% { stroke-width: 4px; }
  381. }
  382. .leaflet-routing-container{position:relative}
  383. .routing-close-btn {
  384.     position: absolute;
  385.     top: 10px;
  386.     right: 10px;
  387.     width: 24px;
  388.     height: 24px;
  389.     background-color: var(--primary);
  390.     color: white;
  391.     border: none;
  392.     border-radius: 50%;
  393.     cursor: pointer;
  394.     display: flex;
  395.     align-items: center;
  396.     justify-content: center;
  397.     font-weight: bold;
  398.     font-size: 14px;
  399.     z-index: 1000;
  400.     transition: background-color 0.3s;
  401. }
  402.  
  403. .routing-close-btn:hover {
  404.     background-color: rgba(255, 26, 26, 0.8);
  405. }
  406. .custom-popup .leaflet-popup-content-wrapper {
  407.     background-color: rgb(255, 255, 255);  /* Fully opaque white background */
  408.     border-radius: 8px;
  409. }
  410.  
  411. .custom-popup .leaflet-popup-tip {
  412.     background-color: rgb(255, 255, 255);  /* Fully opaque white background */
  413. }
  414.  
  415. .custom-popup {
  416.     margin-bottom: 30px;
  417. }
  418.  
  419. .custom-popup .leaflet-popup-close-button {
  420.     color: #ff4d4d;
  421.     font-size: 20px;
  422.     font-weight: bold;
  423.     opacity: 1;
  424.     right: 8px;
  425.     top: 8px;
  426. }
  427.  
  428. .custom-popup .leaflet-popup-close-button:hover {
  429.     color: #ff1a1a;
  430. }
  431.         .hours-info {
  432.             margin-top: 10px;
  433.             font-size: 12px;
  434.             color: #333;
  435.         }
  436.  
  437.         .baloon h3 {
  438.     margin: 0 0 10px 0;
  439.     color: #333;
  440. }
  441.  
  442. .baloon p {
  443.     margin: 0 0 10px 0;
  444.     line-height: 1.4;
  445. }
  446.  
  447. .baloon a {
  448.     display: inline-block;
  449.     text-decoration: none;
  450.     margin-top: 5px;
  451. }
  452.  
  453.  
  454. /* Stile per il riquadro dei risultati */
  455. #search-results {
  456.     display: none; /* Nascondi di default */
  457.     position: relative;
  458.     width: calc(100% - 40px);
  459.     max-height: calc(100% - 150px); /* Adjust based on your header height */
  460.     overflow-y: auto;
  461.     background-color: #fff;
  462.     border-radius: 8px;
  463.     box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  464.     padding: 15px;
  465.     margin: 20px auto;
  466.     border: 1px solid #e0e0e0;
  467.     z-index: 1;
  468. }
  469.        
  470. /* Stile per il titolo del municipio */
  471. #search-results .municipio-title {
  472.   font-size: 16px;
  473.   font-weight: 600;
  474.   color: #333;
  475.   margin-bottom: 10px;
  476.   padding-bottom: 10px;
  477.   border-bottom: 1px solid #e0e0e0;
  478. }
  479.  
  480. /* Stile per ogni voce di biblioteca */
  481. #search-results .library-item {
  482.   padding: 8px 12px;
  483.   margin: 5px 0;
  484.   border-radius: 6px;
  485.   background-color: #f9f9f9;
  486.   cursor: pointer;
  487.   transition: background-color 0.2s ease, transform 0.2s ease;
  488.   font-size: 14px;
  489.   color: #555;
  490.   border: 1px solid #e0e0e0;
  491. }
  492.  
  493. /* Effetto hover sulle voci di biblioteca */
  494. #search-results .library-item:hover {
  495.   background-color: #f1f1f1;
  496.   transform: translateX(5px);
  497.   border-color: #4285f4;
  498. }
  499.  
  500.        
  501. /* Scrollbar personalizzata */
  502. #search-results::-webkit-scrollbar {
  503.   width: 8px;
  504. }
  505.  
  506. #search-results::-webkit-scrollbar-track {
  507.   background: #f1f1f1;
  508.   border-radius: 4px;
  509. }
  510.  
  511. #search-results::-webkit-scrollbar-thumb {
  512.   background: #888;
  513.   border-radius: 4px;
  514. }
  515.  
  516. #search-results::-webkit-scrollbar-thumb:hover {
  517.   background: #555;
  518. }
  519.  
  520. @media (max-width: 768px) {
  521.   .sidebar-expanded.open {
  522.     width: 100%; /* Occupa tutta la larghezza su schermi piccoli */
  523.     padding: 10px; /* Riduci il padding su schermi piccoli */
  524.   }
  525.  
  526.   #search-results {
  527.     width: calc(100% - 20px); /* Riduci la larghezza su schermi piccoli */
  528.     margin: 10px auto; /* Riduci il margine su schermi piccoli */
  529.   }
  530. }        
  531.  
  532. /* Stile per il contenitore del campo di ricerca */
  533. .search-input-wrapper {
  534.     position: relative;
  535.     display: inline-block;
  536.     width: 100%; /* Occupa tutta la larghezza disponibile */
  537. }
  538.  
  539. /* Stile per il campo di ricerca */
  540. #start-point {
  541.     width: 100%; /* Occupa tutta la larghezza del contenitore */
  542.     padding-right: 30px; /* Lascia spazio per la "x" */
  543.     box-sizing: border-box; /* Include il padding nella larghezza totale */
  544. }
  545.  
  546. /* Stile per il pulsante "x" */
  547. .clear-search-btn {
  548.     display: none; /* Inizialmente nascosto */
  549.     position: absolute;
  550.     right: 10px; /* Posizione rispetto al bordo destro del campo */
  551.     top: 50%;
  552.     transform: translateY(-50%);
  553.     cursor: pointer;
  554.     color: #888; /* Colore grigio */
  555.     font-size: 14px;
  556.     background: none;
  557.     border: none;
  558.     padding: 0;
  559.     margin: 0;
  560. }
  561.  
  562. .clear-search-btn:hover {
  563.     color: #555; /* Colore più scuro al passaggio del mouse */
  564. }
  565.        
  566.  
  567. .custom-datalist {
  568.     display: none;
  569.     position: absolute;
  570.     top: 100%;
  571.     left: 0;
  572.     width: 100%;
  573.     max-height: 200px;
  574.     overflow-y: auto;
  575.     background-color: #fff;
  576.     border: 1px solid #ccc;
  577.     border-radius: 4px;
  578.     box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  579.     z-index: 1000;
  580. }
  581.  
  582. .custom-datalist div {
  583.     padding: 8px 12px;
  584.     cursor: pointer;
  585.     transition: background-color 0.2s ease;
  586.     display: block;
  587. }
  588.  
  589. .custom-datalist div:hover {
  590.     background-color: #f1f1f1;
  591. }
  592.  
  593. .custom-datalist div.highlighted {
  594.     background-color: #4285f4;
  595.     color: #fff;
  596. }
  597.  
  598. /* Stile per il contenitore delle biblioteche federate */
  599. .federate-container {
  600.     padding: 20px;
  601. }
  602.  
  603. .federate-container h4 {
  604.    
  605.     margin: 0 0 12px 0;
  606.     font-size: 14px;
  607.     color: #333;
  608. }
  609.  
  610. .federate-container .legend-grid {
  611.     display: grid;
  612.     grid-template-columns: 1fr;
  613.     gap: 6px;
  614.     margin-top: 20px;
  615. }
  616.  
  617.  
  618. .federate-container .legend-item span {
  619.     width: 18px;
  620.     height: 18px;
  621.     margin-right: 10px;
  622.     border-radius: 4px;
  623.     box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
  624.     flex-shrink: 0; /* Impedisce al quadrato di ridimensionarsi */
  625.     margin-top: 3px; /* Aggiunge un piccolo offset verso il basso */
  626. }
  627.  
  628. .federate-container .legend-item:hover {
  629.     background: rgba(241, 242, 246, 0.6);
  630.     transform: translateX(3px);
  631.     border-color: #dfe4ea;
  632. }
  633.  
  634. .federate-container .legend-item .text {
  635.     font-size: 13px;
  636.     color: #4a5568;
  637.     font-weight: 500;
  638.     letter-spacing: 0.02em;
  639.     margin-right: 20px;
  640.     flex: 1; /* Permette al testo di occupare lo spazio rimanente */
  641.     line-height: 1.4; /* Migliora la spaziatura delle righe per il testo su più righe */
  642. }
  643.  
  644.  .legend-item-header {
  645.     display: flex;
  646.     align-items: center;
  647.     width: 100%;
  648. }
  649.  
  650. .libraries-list {
  651.     display: none;
  652.     margin-top: 10px;
  653.     margin-left: 28px;
  654.     font-size: 12px;
  655. }
  656.  
  657. .libraries-list.expanded {
  658.     display: block;
  659. }
  660.  
  661. .library-item-legend {
  662.     padding: 4px 0;
  663.     color: #666;
  664.     cursor: pointer;
  665.     transition: color 0.2s ease;
  666. }
  667.  
  668. .library-item-legend:hover {
  669.     color: #000;
  670. }
  671.  
  672.  
  673.  
  674. .legend-item.expanded .expand-icon {
  675.     transform: rotate(180deg);
  676. }      
  677.  
  678.  
  679.  
  680. /* Aggiungi questi nuovi stili */
  681. .expand-button {
  682.     width: 20px;
  683.     height: 20px;
  684.     background-color: #f0f0f0;
  685.     border: 1px solid #ddd;
  686.     border-radius: 50%;
  687.     position: absolute;
  688.     right: 10px;
  689.     top: 50%;
  690.     transform: translateY(-50%);
  691.     cursor: pointer;
  692.     display: flex;
  693.     align-items: center;
  694.     justify-content: center;
  695.     transition: all 0.2s ease;
  696. }
  697.  
  698. .expand-button:hover {
  699.     background-color: #e0e0e0;
  700. }
  701.  
  702. .expand-button::after {
  703.     content: '+';
  704.     font-size: 16px;
  705.     color: #666;
  706. }
  707.  
  708. .legend-item.expanded .expand-button::after {
  709.     content: '−';
  710. }
  711.  
  712.        
  713.     </style>
  714. </head>
  715. <body>
  716.  
  717.  
  718.    
  719.     <!-- Barra laterale fissa -->
  720. <div class=sidebar>
  721.  
  722. <button id=searchBtn onclick=toggleSearch() data-tooltip="Cerca biblioteca o indirizzo">
  723. <img src=search-icon.svg alt="Cerca biblioteca o indirizzo">
  724. </button>
  725. <button id="legendBtn" onclick="toggleLegendSidebar()" data-tooltip="Mostra/Nascondi legenda">
  726.     <img src="legend-icon.svg" alt="Mostra/Nascondi legenda">
  727. </button>
  728.  
  729.  <!-- Nuovo bottone con icona federate-icon.svg -->
  730.     <button id="federateBtn" onclick="toggleFederateSidebar()" data-tooltip="Biblioteche federate">
  731.         <img src="federate-icon.svg" alt="Biblioteche federate">
  732.     </button>  
  733. </div>
  734.  
  735.  
  736. <!-- Terzo pannello laterale espanso: legenda municipi -->
  737. <div class="sidebar-expanded" id="legendSidebar">
  738.     <div class="sidebar-close-icon" onclick="toggleLegendSidebar()"></div>
  739.     <div class="legend-container">
  740.         <h4>Mappa Biblioteche di Roma per Municipio</h4>
  741.         <div class="legend-grid">
  742.             <!-- Il contenuto della legenda verrà popolato dinamicamente dal JavaScript -->
  743.         </div>
  744.     </div>
  745. </div>
  746.    
  747.  
  748. <!-- Seconda barra laterale (espansione) -->
  749. <div class="sidebar-expanded" id="expandedSidebar">
  750.     <div class="sidebar-close-icon" onclick="toggleExpandedSidebar()"></div>
  751.     <!-- Aggiungi questa linea -->
  752. <div class="search-container" id="searchContainer">
  753.   <div style="display: flex; align-items: center; gap: 10px;">
  754.     <a href="https://www.bibliotechediroma.it/opac/library/Biblioteca%20Elsa%20Morante/RMBO2" title="Vai alla Biblioteca Elsa Morante">
  755.       <img src="home.svg" alt="Biblioteca Home" class="icon">
  756.     </a>
  757.     <div class="search-input-wrapper" style="flex: 1;">
  758.       <input id="start-point" placeholder="Inserisci punto di partenza" list="library-suggestions">
  759.       <span id="clear-search" class="clear-search-btn" onclick="clearSearchField()"></span>
  760.     </div>
  761.   </div>
  762.   <div id="library-suggestions" class="custom-datalist"></div>
  763. <div style="display: flex; gap: 10px;">
  764.   <button onclick="searchRoute()" style="flex: 1;">Cerca</button>
  765.   <button onclick="resetMap()" style="flex: 1; background-color: #6B7280;">Reset</button>
  766. </div>
  767.  
  768.  
  769. <!-- Nuovo pannello per le biblioteche federate -->
  770. <div class="sidebar-expanded" id="federateSidebar">
  771.     <div class="sidebar-close-icon" onclick="toggleFederateSidebar()"></div>
  772. <div class="federate-container">
  773.     <h4>Biblioteche Federate</h4>
  774.     <div class="legend-grid">
  775.         <div class="legend-item" onclick="zoomToSystemLibraries('CASTELLI ROMANI')">
  776.             <span style="background: #0006fd"></span>
  777.             <div class="text">Biblioteche Sistema Castelli Romani</div>
  778.         </div>
  779.         <div class="legend-item" onclick="zoomToSystemLibraries('Ceretano-Sabatino')">
  780.             <span style="background: #fc04f7"></span>
  781.             <div class="text">Sistema Bibliotecario Ceretano-Sabatino</div>
  782.         </div>
  783.         <div class="legend-item" onclick="zoomToSystemLibraries('Monti Prenestini')">
  784.             <span style="background: #844715"></span>
  785.             <div class="text">Sistema Bibliotecario Monti Prenestini</div>
  786.         </div>
  787.         <div class="legend-item" onclick="zoomToSystemLibraries('LUMSA')">
  788.             <span style="background: #1e8b1f"></span>
  789.             <div class="text">Lumsa</div>
  790.         </div>
  791.         <div class="legend-item" onclick="zoomToSystemLibraries('Foro Italico')">
  792.             <span style="background: #473e86"></span>
  793.             <div class="text">Foro Italico</div>
  794.         </div>
  795.         <div class="legend-item" onclick="zoomToSystemLibraries('Link')">
  796.             <span style="background: #2f4d4c"></span>
  797.             <div class="text">Link Campus</div>
  798.         </div>
  799.         <div class="legend-item" onclick="zoomToSystemLibraries('La Sapienza')">
  800.             <span style="background: #86134f"></span>
  801.             <div class="text">Sistema Bibliotecario di Ateneo<br>Università degli Studi Roma La Sapienza</div>
  802.         </div>
  803.         <div class="legend-item" onclick="zoomToSystemLibraries('Tor Vergata')">
  804.             <span style="background: #382724"></span>
  805.             <div class="text">Sistema Bibliotecario di Ateneo<br>Università degli Studi Roma Tor Vergata</div>
  806.         </div>
  807.         <div class="legend-item" onclick="zoomToSystemLibraries('Roma Tre')">
  808.             <span style="background: #f20000"></span>
  809.             <div class="text">Sistema Bibliotecario di Ateneo<br>Università degli Studi Roma Tre</div>
  810.         </div>
  811.     </div>
  812.  
  813.  
  814.  
  815. <button id="toggleFederatedLibraries" onclick="toggleFederatedLibraries()" style="width: 100%; margin-top: 15px; padding: 8px; background-color: var(--primary); color: white; border: none; border-radius: 4px; cursor: pointer;">
  816.     Mostra Biblioteche Federate
  817. </button>
  818.            
  819.             <button id="toggleRomeLibraries" onclick="toggleRomeLibraries()" style="width: 100%; margin-top: 15px; padding: 8px; background-color: var(--primary); color: white; border: none; border-radius: 4px; cursor: pointer;">
  820.     Rimuovi Biblioteche di Roma
  821. </button>
  822.  
  823.            
  824.            
  825.         </div>
  826.     </div>
  827. </div>
  828.  
  829.  
  830.    
  831.     <div id=search-results></div>
  832. </div>
  833.  
  834.     <div id="map"></div>
  835.    
  836.     <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
  837.     <script src="https://unpkg.com/leaflet-routing-machine@3.2.12/dist/leaflet-routing-machine.js"></script>
  838.     <script src="icons-config.js"></script> <!-- Aggiungi questa riga -->
  839.     <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
  840.    
  841.     <script>
  842.  
  843.  // Colori per i diversi municipi
  844. const municipalityColors = {
  845.     1: "#6a3f3a", // Marrone
  846.     2: "#00faff", // Turchese
  847.     3: "#fbd51c", // Giallo
  848.     4: "#473e86", // Blu scuro
  849.     5: "#018a87", // Verde acqua
  850.     6: "#d33682", // Rosa
  851.     7: "#ff7c4e", // Arancione
  852.     8: "#22a825", // Verde erba
  853.     9: "#2b83cb", // Blu cielo
  854.     10: "#cb283c", // Rosso
  855.     11: "#cac12e", // Giallo oliva
  856.     12: "#a329ca", // Viola
  857.     13: "#c9832c", // Arancione-marrone
  858.     14: "#7b7b79", // Grigio
  859.     15: "#000000"  // Nero
  860. };
  861.  
  862.  
  863.  
  864.               // Funzione per convertire numeri in numeri romani
  865.         function romanize(num) {
  866.             const roman = ['I','II','III','IV','V','VI','VII','VIII','IX','X','XI','XII','XIII','XIV','XV'];
  867.             return roman[num - 1] || num;
  868.         }
  869.  
  870. // Initialize global variables
  871. let map;
  872. let currentBorder = null;
  873. let routingControl = null;
  874. let municipioLayer = null;
  875. let municipioLayers = {};
  876. let currentHighlight = null;
  877. let selectedMunicipio = null;
  878. let romeLibrariesVisible = true;
  879. let federatedLibrariesVisible = false;
  880. let removedRomeLibraries = [];
  881. let removedFederatedLibraries = [];
  882.  
  883.       // Funzione per aprire/chiudere la seconda barra laterale
  884.         function toggleExpandedSidebar() {
  885.             const expandedSidebar = document.getElementById('expandedSidebar');
  886.             expandedSidebar.classList.toggle('open');
  887.         }
  888.  
  889. // Add this new function for legend toggle
  890. function toggleLegendSidebar() {
  891.     const legendSidebar = document.getElementById('legendSidebar');
  892.     const legendBtn = document.getElementById('legendBtn');
  893.     const expandedSidebar = document.getElementById('expandedSidebar');
  894.     const federateSidebar = document.getElementById('federateSidebar');
  895.     const searchContainer = document.getElementById('searchContainer');
  896.  
  897.     // Rimuovo la classe active da tutti i pulsanti
  898.     document.querySelectorAll('.sidebar button').forEach(button => {
  899.         button.classList.remove('active');
  900.     });
  901.  
  902.     // Chiudo gli altri pannelli
  903.     expandedSidebar.classList.remove('open');
  904.     federateSidebar.classList.remove('open');
  905.     if (searchContainer) {
  906.         searchContainer.style.display = 'none';
  907.     }
  908.  
  909.     // Gestisco l'apertura/chiusura del pannello legenda
  910.     legendSidebar.classList.toggle('open');
  911.     if (legendSidebar.classList.contains('open')) {
  912.         legendBtn.classList.add('active');
  913.     } else {
  914.         legendBtn.classList.remove('active');
  915.     }
  916. }
  917.  
  918. // Modifica la funzione toggleLegend esistente per chiamare toggleLegendSidebar
  919. function toggleLegend() {
  920.     toggleLegendSidebar();
  921. }
  922.  
  923.  
  924. // Funzione per cercare una biblioteca e aprire il popup corrispondente
  925. async function searchLibrary() {
  926.     const searchTerm = document.getElementById('start-point').value.toLowerCase();
  927.     if (!searchTerm) return;
  928.  
  929.     try {
  930.         const response = await fetch('libraries.json');
  931.         const data = await response.json();
  932.  
  933.         // Trova la biblioteca cercata
  934.         const foundLibrary = data.libraries.find(library =>
  935.             library.name.toLowerCase().includes(searchTerm)
  936.         );
  937.  
  938.         if (foundLibrary) {
  939.             // Trova tutte le biblioteche dello stesso municipio
  940.             const librariesInMunicipio = data.libraries.filter(library =>
  941.                 library.municipality === foundLibrary.municipality
  942.             );
  943.  
  944.             // Mostra il municipio e le biblioteche
  945.             showMunicipioAndLibraries(foundLibrary.municipality, librariesInMunicipio);
  946.         } else {
  947.             alert('Biblioteca non trovata. Riprova.');
  948.         }
  949.     } catch (error) {
  950.         console.error('Errore nella ricerca della biblioteca:', error);
  951.         alert('Errore nella ricerca. Riprova.');
  952.     }
  953. }
  954.  
  955. function showMunicipioAndLibraries(municipioNumber, libraries) {
  956.     const resultsContainer = document.getElementById('search-results');
  957.    
  958.     // Svuota il contenuto precedente
  959.     resultsContainer.innerHTML = '';
  960.  
  961.     // Aggiungi il titolo del municipio
  962.     const municipioDiv = document.createElement('div');
  963.     municipioDiv.className = 'municipio-title';
  964.     municipioDiv.textContent = `Municipio ${romanize(municipioNumber)}`;
  965.     resultsContainer.appendChild(municipioDiv);
  966.  
  967.     // Aggiungi le biblioteche del municipio
  968.     libraries.forEach(library => {
  969.         const libraryDiv = document.createElement('div');
  970.         libraryDiv.className = 'library-item';
  971.         libraryDiv.textContent = library.name;
  972.         libraryDiv.onclick = () => selectLibrary(library);
  973.         resultsContainer.appendChild(libraryDiv);
  974.     });
  975.  
  976.     // Mostra il riquadro dei risultati solo se ci sono biblioteche
  977.     if (libraries.length > 0) {
  978.         resultsContainer.style.display = 'block';
  979.     } else {
  980.         resultsContainer.style.display = 'none';
  981.     }
  982.  
  983.     // Evidenzia il municipio sulla mappa
  984.     loadAndShowBorder(municipioNumber, map);
  985.     highlightLegendItem(municipioNumber);
  986.     selectedMunicipio = municipioNumber;
  987. }
  988.  
  989. function selectLibrary(library) {
  990.     let foundMarker = null;
  991.     map.eachLayer((layer) => {
  992.         if (layer instanceof L.Marker) {
  993.             const pos = layer.getLatLng();
  994.             if (pos.lat === library.coords[0] && pos.lng === library.coords[1]) {
  995.                foundMarker = layer;
  996.             }
  997.         }
  998.     });
  999.  
  1000.     if (foundMarker) {
  1001.         foundMarker.openPopup();
  1002.         map.setView(foundMarker.getLatLng(), 14);
  1003.         loadAndShowBorder(library.municipality, map);
  1004.         highlightLegendItem(library.municipality);
  1005.         selectedMunicipio = library.municipality;
  1006.     } else {
  1007.         console.log('Marker not found for library:', library);
  1008.     }
  1009.  
  1010.     // Nascondi il riquadro dei risultati
  1011.     const resultsContainer = document.getElementById('search-results');
  1012.     if (resultsContainer) {
  1013.         resultsContainer.style.display = 'none';
  1014.     }
  1015. }
  1016.  
  1017. // Funzione per aprire/chiudere il pannello delle biblioteche federate
  1018. function toggleFederateSidebar() {
  1019.     const federateSidebar = document.getElementById('federateSidebar');
  1020.     const federateBtn = document.getElementById('federateBtn');
  1021.     const expandedSidebar = document.getElementById('expandedSidebar');
  1022.     const searchContainer = document.getElementById('searchContainer');
  1023.  
  1024.     // Rimuovo la classe active da tutti i pulsanti
  1025.     document.querySelectorAll('.sidebar button').forEach(button => {
  1026.         button.classList.remove('active');
  1027.     });
  1028.  
  1029.     // Chiudo il pannello di ricerca se aperto
  1030.     expandedSidebar.classList.remove('open');
  1031.     if (searchContainer) {
  1032.         searchContainer.style.display = 'none';
  1033.     }
  1034.  
  1035.     // Gestisco l'apertura/chiusura del pannello federate
  1036.     federateSidebar.classList.toggle('open');
  1037.     if (federateSidebar.classList.contains('open')) {
  1038.         federateBtn.classList.add('active');
  1039.     } else {
  1040.         federateBtn.classList.remove('active');
  1041.     }
  1042. }
  1043.  
  1044. function toggleSearch() {
  1045.     const searchBtn = document.getElementById('searchBtn');
  1046.     const expandedSidebar = document.getElementById('expandedSidebar');
  1047.     const searchContainer = document.getElementById('searchContainer');
  1048.     const federateSidebar = document.getElementById('federateSidebar');
  1049.     const federateBtn = document.getElementById('federateBtn');
  1050.  
  1051.     // Rimuovo la classe active da tutti i pulsanti eccetto searchBtn
  1052.     document.querySelectorAll('.sidebar button').forEach(button => {
  1053.         if (button !== searchBtn) {
  1054.             button.classList.remove('active');
  1055.         }
  1056.     });
  1057.  
  1058.     // Chiudo il pannello federate se aperto
  1059.     federateSidebar.classList.remove('open');
  1060.     federateBtn.classList.remove('active');
  1061.  
  1062.     // Verifico se il pannello di ricerca è già aperto
  1063.     const isSearchOpen = expandedSidebar.classList.contains('open');
  1064.  
  1065.     if (isSearchOpen) {
  1066.         // Se il pannello di ricerca è aperto, lo chiudo
  1067.         expandedSidebar.classList.remove('open');
  1068.         searchBtn.classList.remove('active');
  1069.         searchContainer.style.display = 'none';
  1070.     } else {
  1071.         // Se il pannello di ricerca è chiuso, lo apro
  1072.         expandedSidebar.classList.add('open');
  1073.         searchBtn.classList.add('active');
  1074.  
  1075.         const startPoint = document.getElementById('start-point');
  1076.         startPoint.placeholder = "Cerca biblioteca o indirizzo...";
  1077.  
  1078.         const searchButton = searchContainer.querySelector('button');
  1079.         searchButton.textContent = "Cerca";
  1080.         searchButton.onclick = async function() {
  1081.             const searchQuery = startPoint.value.toLowerCase();
  1082.             try {
  1083.                 const response = await fetch('libraries.json');
  1084.                 const data = await response.json();
  1085.                 const foundLibrary = data.libraries.find(lib =>
  1086.                     lib.name.toLowerCase().includes(searchQuery)
  1087.                 );
  1088.                 if (foundLibrary) {
  1089.                     const municipioLibraries = data.libraries.filter(lib =>
  1090.                         lib.municipality === foundLibrary.municipality
  1091.                     );
  1092.                     showMunicipioAndLibraries(foundLibrary.municipality, municipioLibraries);
  1093.                     return;
  1094.                 }
  1095.             } catch (error) {
  1096.                 console.error('Errore nella ricerca della biblioteca:', error);
  1097.             }
  1098.             searchRoute();
  1099.         };
  1100.  
  1101.         searchContainer.style.display = 'block';
  1102.     }
  1103. }
  1104.  
  1105. // Funzione per resettare il campo di ricerca
  1106. function clearSearchField() {
  1107.     const searchInput = document.getElementById('start-point');
  1108.     searchInput.value = '';
  1109.     searchInput.focus();
  1110.     toggleClearButton();
  1111. }
  1112.  
  1113. // Funzione per mostrare/nascondere il pulsante "x"
  1114. function toggleClearButton() {
  1115.     const searchInput = document.getElementById('start-point');
  1116.     const clearButton = document.getElementById('clear-search');
  1117.     if (searchInput.value.trim() !== '') {
  1118.         clearButton.style.display = 'inline-block'; // Mostra la "x"
  1119.     } else {
  1120.         clearButton.style.display = 'none'; // Nascondi la "x"
  1121.     }
  1122. }
  1123.  
  1124. // Aggiungi un listener per l'input nel campo di ricerca
  1125. document.getElementById('start-point').addEventListener('input', toggleClearButton);
  1126.  
  1127. // Aggiungi un listener per l'input nel campo di ricerca
  1128. document.getElementById('start-point').addEventListener('input', toggleClearButton);
  1129.        
  1130.        
  1131. // Funzione helper per caricare e visualizzare il confine di un municipio
  1132. async function loadAndShowBorder(municipalityNumber, map) {
  1133.     // Se c'è già un confine visualizzato, rimuovilo
  1134.     if (currentBorder) {
  1135.         map.removeLayer(currentBorder);
  1136.     }
  1137.  
  1138.     try {
  1139.         const response = await fetch(`municipio${municipalityNumber}.geojson`);
  1140.         const data = await response.json();
  1141.        
  1142.        
  1143.  
  1144.         currentBorder = L.geoJSON(data, {
  1145.             style: {
  1146.                 color: municipalityColors[municipalityNumber],
  1147.                 weight: 3,
  1148.                 opacity: 0.65,
  1149.                 fillOpacity: 0.2  
  1150.             }
  1151.         }).addTo(map);
  1152.  
  1153.         map.fitBounds(currentBorder.getBounds());
  1154.     } catch (error) {
  1155.         console.error(`Errore nel caricamento del confine del municipio ${municipalityNumber}:`, error);
  1156.     }
  1157. }
  1158.        
  1159.  
  1160.  
  1161.  
  1162.  
  1163.  
  1164. // Funzione per evidenziare la voce della legenda
  1165. function highlightLegendItem(municipioNumber) {
  1166.   const allItems = document.querySelectorAll('.legend-item');
  1167.   allItems.forEach(item => item.classList.remove('highlighted'));
  1168.   const item = document.querySelector(`.legend-item[data-municipio="${municipioNumber}"]`);
  1169.   if (item) {
  1170.     item.classList.add('highlighted');
  1171.   }
  1172. }
  1173. window.highlightLegendItem = highlightLegendItem;
  1174.  
  1175.        
  1176. document.addEventListener("DOMContentLoaded", async function() {
  1177.     // Initialize map
  1178.     const center = [41.732522, 12.271879];
  1179.     map = L.map("map", {
  1180.         zoomControl: false
  1181.     }).setView(center, 14);
  1182.            
  1183.     // Add zoom control to the right side with custom CSS class
  1184.     const zoomControl = L.control.zoom({
  1185.         position: 'topright'
  1186.     });
  1187.    
  1188.     zoomControl.addTo(map);
  1189.    
  1190.     // Add custom CSS to the zoom control container after it's added to the map
  1191.     const zoomContainer = zoomControl.getContainer();
  1192.     zoomContainer.style.marginTop = '22px';
  1193.  
  1194.     L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
  1195.         maxZoom: 19,
  1196.         attribution: "© OpenStreetMap contributors"
  1197.     }).addTo(map);
  1198.  
  1199.    
  1200.     // Definizione del percorso
  1201.     const pathPoints = [
  1202.         center,
  1203.         [41.732154, 12.271675],
  1204.         [41.732282, 12.271254],
  1205.         [41.73224, 12.27123],
  1206.         [41.732276, 12.27109],
  1207.         [41.732092, 12.27098]
  1208.     ];
  1209.  
  1210.     // Creazione della linea tratteggiata
  1211.     const dottedLine = L.polyline(pathPoints, {
  1212.         color: "#0000FF",
  1213.         weight: 4,
  1214.         opacity: 0.8,
  1215.         dashArray: "10, 10"
  1216.     }).addTo(map);
  1217.  
  1218.     // Punto finale del percorso
  1219.     const endPoint = pathPoints[pathPoints.length - 1];
  1220.  
  1221.  
  1222.    
  1223.     // Definizione della funzione createMarker
  1224. function createMarker(coords, title, icon, popupContent, municipioNumber, map) {
  1225.     const marker = L.marker(coords, { title, icon });
  1226.  
  1227.     if (map) {
  1228.         marker.addTo(map);
  1229.     }
  1230.  
  1231.     marker.on('click', () => {
  1232.         if (selectedMunicipio === municipioNumber) {
  1233.             // Deseleziona se cliccato nuovamente
  1234.             if (currentBorder) {
  1235.                 map.removeLayer(currentBorder);
  1236.                 currentBorder = null;
  1237.             }
  1238.             selectedMunicipio = null;
  1239.             const item = document.querySelector(`.legend-item[data-municipio="${municipioNumber}"]`);
  1240.             if (item) {
  1241.                 item.classList.remove('highlighted');
  1242.             }
  1243.         } else {
  1244.             // Seleziona il municipio
  1245.             loadAndShowBorder(municipioNumber, map);
  1246.             highlightLegendItem(municipioNumber);
  1247.             selectedMunicipio = municipioNumber;
  1248.         }
  1249.     });
  1250.  
  1251.     const popup = L.popup({
  1252.         className: 'custom-popup',
  1253.         offset: [0, -10],
  1254.         closeButton: true
  1255.     }).setContent(popupContent);
  1256.  
  1257.     marker.bindPopup(popup);
  1258.     return marker;
  1259. }
  1260.    
  1261. const icons = getIconsConfig();
  1262.  
  1263.  
  1264. // Spazio dove erano i marker per le biblioteche...
  1265. // Carica le biblioteche dal file JSON
  1266. // Dopo aver caricato le biblioteche dal file JSON
  1267. try {
  1268.     const response = await fetch('libraries.json');
  1269.     const data = await response.json();
  1270.  
  1271. // Add this line to make data globally accessible
  1272.     window.libraryData = data;
  1273.    
  1274.     // Popola il datalist con i nomi delle biblioteche
  1275. const customDatalist = document.getElementById('library-suggestions');
  1276.     // Aggiungi sia le biblioteche normali che quelle federate al datalist
  1277.     [...data.libraries, ...data.federate].forEach(library => {
  1278.         const option = document.createElement('div');
  1279.         option.textContent = library.name;
  1280.         option.onclick = () => {
  1281.             document.getElementById('start-point').value = library.name;
  1282.             customDatalist.style.display = 'none';
  1283.             searchLibrary();
  1284.         };
  1285.         customDatalist.appendChild(option);
  1286.     });
  1287.  
  1288. // Mostra/nascondi il menu personalizzato quando il campo di ricerca è focalizzato
  1289. document.getElementById('start-point').addEventListener('focus', () => {
  1290.     customDatalist.style.display = 'block';
  1291. });
  1292.  
  1293. document.getElementById('start-point').addEventListener('blur', () => {
  1294.     setTimeout(() => {
  1295.         customDatalist.style.display = 'none';
  1296.     }, 200);
  1297. });
  1298.  
  1299.  
  1300.  
  1301. function filterSuggestions(searchText) {
  1302.     const customDatalist = document.getElementById('library-suggestions');
  1303.     const options = customDatalist.querySelectorAll('div');
  1304.  
  1305.     options.forEach(option => {
  1306.         const libraryName = option.textContent.toLowerCase();
  1307.         if (libraryName.includes(searchText.toLowerCase())) {
  1308.             option.style.display = 'block'; // Mostra l'opzione
  1309.         } else {
  1310.             option.style.display = 'none'; // Nascondi l'opzione
  1311.         }
  1312.     });
  1313.  
  1314.     // Mostra il menu solo se ci sono opzioni visibili
  1315.     const visibleOptions = Array.from(options).filter(option => option.style.display !== 'none');
  1316.     if (visibleOptions.length > 0) {
  1317.         customDatalist.style.display = 'block';
  1318.     } else {
  1319.         customDatalist.style.display = 'none';
  1320.     }
  1321. }
  1322.  
  1323.  
  1324. document.getElementById('start-point').addEventListener('input', (e) => {
  1325.     const searchText = e.target.value;
  1326.     filterSuggestions(searchText);
  1327. });    
  1328.  
  1329.  
  1330. document.addEventListener('click', (e) => {
  1331.     const customDatalist = document.getElementById('library-suggestions');
  1332.     const searchInput = document.getElementById('start-point');
  1333.  
  1334.     if (e.target !== searchInput && e.target !== customDatalist) {
  1335.        customDatalist.style.display = 'none';
  1336.     }
  1337. });
  1338.  
  1339.  
  1340.    
  1341.  
  1342.     // Crea i marker per ogni biblioteca
  1343.     data.libraries.forEach(library => {
  1344.         const popupContent = `
  1345.             <div class="baloon">
  1346.                 <h3>${library.name}</h3>
  1347.                 <p style="font-size: 15px;">
  1348.                     ${library.address} <br>
  1349.                     ${library.cap} Municipio ${library.municipalityName}
  1350.                 </p>
  1351.                 <a style="color: #bc0b34; font-size: 16px;" href="${library.url}">
  1352.                     Vai alla scheda
  1353.                 </a>
  1354.             </div>
  1355.         `;
  1356.         createMarker(
  1357.             library.coords,
  1358.             library.name,
  1359.             icons[`municipio${library.municipality}`],
  1360.             popupContent,
  1361.             library.municipality,
  1362.             map
  1363.         );
  1364.     });
  1365.  
  1366.  
  1367.  // Crea i marker per le biblioteche federate
  1368. // Memorizza i marker delle biblioteche federate in un array
  1369. window.federatedMarkers = data.federate.map(library => {
  1370.     const popupContent = `
  1371.         <div class="baloon">
  1372.             <h3>${library.name}</h3>
  1373.             <p style="font-size: 15px;">
  1374.                 ${library.address}<br>
  1375.                 ${library.libraryType} - ${library.system}
  1376.             </p>
  1377.             <a style="color: #bc0b34; font-size: 16px;" href="${library.url}">
  1378.                 Vai alla scheda
  1379.             </a>
  1380.         </div>
  1381.     `;
  1382.     // Seleziona l'icona appropriata in base al sistema bibliotecario
  1383.     const iconKey = library.system.includes('Roma Tre') ? 'uniroma3' :
  1384.                     library.system.includes('CASTELLI ROMANI') ? 'castelli' :
  1385.                     library.system.includes('Ceretano-Sabatino') ? 'ceretano' :
  1386.                     library.system.includes('Monti Prenestini') ? 'prenestini' :
  1387.                     library.system.includes('LUMSA') ? 'lumsa' :
  1388.                     library.system.includes('Foro Italico') ? 'foro' :
  1389.                     library.system.includes('Tor Vergata') ? 'vergata' :
  1390.                     library.system.includes('La Sapienza') ? 'sapienza' :
  1391.                     library.system.includes('Link') ? 'link' : 'municipio1'; // fallback icon
  1392.  
  1393.     return createMarker(
  1394.         library.coords,
  1395.         library.name,
  1396.         icons[iconKey],
  1397.         popupContent,
  1398.         null, // non c'è un municipio associato
  1399.         null  // non aggiungere alla mappa inizialmente
  1400.     );
  1401. });
  1402.    
  1403.  
  1404.    
  1405. } catch (error) {
  1406.     console.error('Errore nel caricamento delle biblioteche:', error);
  1407. }
  1408.    
  1409.  
  1410.  
  1411.    
  1412.  
  1413.  
  1414.  
  1415.  
  1416.    async function showMunicipioI() {
  1417.   // Rimuovi il layer precedente se esiste
  1418.   if (municipioLayer) {
  1419.     map.removeLayer(municipioLayer);
  1420.   }
  1421.  
  1422.   try {
  1423.     const response = await fetch('municipio1.geojson');
  1424.     const data = await response.json();
  1425.    
  1426.     municipioLayer = L.geoJSON(data, {
  1427.       style: {
  1428.         color: '#6a3f3a',
  1429.         weight: 3,
  1430.         opacity: 0.65,
  1431.         fillOpacity: 0
  1432.       }
  1433.     }).addTo(map);
  1434.    
  1435.     map.fitBounds(municipioLayer.getBounds());
  1436.   } catch (error) {
  1437.     console.error('Errore nel caricamento del confine:', error);
  1438.   }
  1439. }
  1440.  
  1441.  
  1442.  
  1443.  
  1444.  
  1445.    
  1446.             window.searchRoute = async function() {
  1447.                 const startPoint = document.getElementById('start-point').value;
  1448.                
  1449.                 if (routingControl) {
  1450.                     map.removeControl(routingControl);
  1451.                 }
  1452.  
  1453.                 try {
  1454.                     const response = await axios.get('https://nominatim.openstreetmap.org/search', {
  1455.                         params: {
  1456.                             q: startPoint,
  1457.                             format: 'json',
  1458.                             limit: 1
  1459.                         }
  1460.                     });
  1461.  
  1462.                     if (response.data.length > 0) {
  1463.                         const startCoords = [
  1464.                             parseFloat(response.data[0].lat),
  1465.                             parseFloat(response.data[0].lon)
  1466.                         ];
  1467.  
  1468.                         routingControl = L.Routing.control({
  1469.                             waypoints: [
  1470.                                 L.latLng(startCoords[0], startCoords[1]),
  1471.                                 L.latLng(center[0], center[1])
  1472.                             ],
  1473.                             routeWhileDragging: true,
  1474.                             language: 'it',
  1475.                             show: true,
  1476.                             addWaypoints: false,
  1477.                             draggableWaypoints: false,
  1478.                             fitSelectedRoutes: true,
  1479.                             lineOptions: {
  1480.                                 styles: [{color: '#0000FF', opacity: 0.8, weight: 5}]
  1481.                             },
  1482.                             createMarker: function(i, waypoint, n) {
  1483.                                 const closeBtn = document.createElement('button');
  1484.                                 closeBtn.innerHTML = '✕';
  1485.                                 closeBtn.className = 'routing-close-btn';
  1486.                                 closeBtn.onclick = function() {
  1487.                                     map.removeControl(routingControl);
  1488.                                 };
  1489.  
  1490.                                 // Append close button to routing container
  1491.                                 setTimeout(() => {
  1492.                                     const container = document.querySelector('.leaflet-routing-container');
  1493.                                     if (container) {
  1494.                                         container.appendChild(closeBtn);
  1495.                                     }
  1496.                                 }, 100);
  1497.  
  1498.                                 return null; // No markers for waypoints
  1499.                             }
  1500.                         }).addTo(map);
  1501.                     } else {
  1502.                         alert('Indirizzo non trovato. Riprova.');
  1503.                     }
  1504.                 } catch (error) {
  1505.                     console.error('Errore nella ricerca del percorso:', error);
  1506.                     alert('Errore nel trovare il percorso. Riprova.');
  1507.                 }
  1508.             }
  1509.  
  1510. document.getElementById('start-point').addEventListener('keypress', function(event) {
  1511.     if (event.key === 'Enter') {
  1512.         const searchButton = document.querySelector('#searchContainer button');
  1513.         if (searchButton.textContent === "Cerca Biblioteca") {
  1514.             searchLibrary();
  1515.         } else {
  1516.             searchRoute();
  1517.         }
  1518.     }
  1519. });
  1520.  
  1521.             document.getElementById('start-point').focus();
  1522.  
  1523.  
  1524.  
  1525. // Fetch and display the municipality border
  1526. fetch('municipio10.geojson')
  1527.     .then(response => response.json())
  1528.     .then(data => {
  1529.  
  1530.  
  1531.        
  1532.  
  1533.         // Carica il municipio X di default
  1534.         municipioLayers[10] = L.geoJSON(data, {
  1535.             style: {
  1536.                 color: municipalityColors[10],
  1537.                 weight: 3,
  1538.                 opacity: 0.65,
  1539.                 fillOpacity: 0
  1540.             }
  1541.         }).addTo(map);
  1542.  
  1543.  
  1544. // Popola la legenda
  1545. // Modifica questa parte nel codice JavaScript dove viene popolata la legenda
  1546. const legendContainer = document.querySelector('.legend-container .legend-grid');
  1547. if (legendContainer) {
  1548.     legendContainer.innerHTML = Object.entries(municipalityColors).map(([num, color]) => {
  1549.         const municipioLibraries = window.libraryData.libraries
  1550.             .filter(lib => lib.municipality === parseInt(num))
  1551.             .map(lib => `<div class="library-item-legend" onclick="selectLibrary(${JSON.stringify(lib)})">${lib.name}</div>`)
  1552.             .join('');
  1553.        
  1554.         return `
  1555.             <div class="legend-item" data-municipio="${num}">
  1556.                 <div class="legend-item-header" onclick="handleMunicipioClick(${num}, event)">
  1557.                     <span style="background: ${color}"></span>
  1558.                     <div class="text">Municipio ${romanize(parseInt(num))}</div>
  1559.                 </div>
  1560.                 <button class="expand-button" onclick="toggleLibrariesList(${num}, event)"></button>
  1561.                 <div class="libraries-list">
  1562.                     ${municipioLibraries}
  1563.                 </div>
  1564.             </div>
  1565.         `;
  1566.     }).join('');
  1567. }
  1568.  
  1569. // Aggiungi queste nuove funzioni
  1570. window.handleMunicipioClick = function(municipioNumber, event) {
  1571.     event.stopPropagation();
  1572.     // Gestisci solo la selezione del municipio
  1573.     handleLegendClick(municipioNumber, null);
  1574. };
  1575.  
  1576. window.toggleLibrariesList = function(municipioNumber, event) {
  1577.     event.stopPropagation();
  1578.     const legendItem = document.querySelector(`.legend-item[data-municipio="${municipioNumber}"]`);
  1579.     const librariesList = legendItem.querySelector('.libraries-list');
  1580.    
  1581.     legendItem.classList.toggle('expanded');
  1582.     librariesList.classList.toggle('expanded');
  1583. };
  1584.  
  1585.  
  1586.        
  1587.  
  1588.         // Gestore hover sulla legenda
  1589. // Funzione per gestire l'hover sulla legenda
  1590. window.handleLegendHover = async function(municipioNumber) {
  1591.                 // Non mostrare l'hover se il municipio è già selezionato
  1592.                 if (municipioNumber === selectedMunicipio) return;
  1593.  
  1594.                 const item = document.querySelector(`.legend-item[data-municipio="${municipioNumber}"]`);
  1595.                 item.classList.add('highlighted');
  1596.  
  1597.                 if (!municipioLayers[municipioNumber]) {
  1598.                     try {
  1599.                         const response = await fetch(`municipio${municipioNumber}.geojson`);
  1600.                         const data = await response.json();
  1601.                         municipioLayers[municipioNumber] = L.geoJSON(data, {
  1602.                             style: {
  1603.                                 color: municipalityColors[municipioNumber],
  1604.                                 weight: 4,
  1605.                                 opacity: 0.9,
  1606.                                 fillOpacity: 0.1
  1607.                             }
  1608.                         });
  1609.                     } catch (error) {
  1610.                         console.error(`Error loading municipality ${municipioNumber}:`, error);
  1611.                         return;
  1612.                     }
  1613.                 }
  1614.  
  1615.                 if (currentHighlight && currentHighlight !== currentBorder) {
  1616.                    map.removeLayer(currentHighlight);
  1617.                 }
  1618.                
  1619.                 currentHighlight = municipioLayers[municipioNumber].addTo(map).bringToFront();
  1620.             };
  1621.  
  1622.             // Funzione per gestire l'uscita del mouse dalla legenda
  1623.             window.handleLegendOut = function(municipioNumber) {
  1624.                 if (municipioNumber === selectedMunicipio) return;
  1625.  
  1626.                 const item = document.querySelector(`.legend-item[data-municipio="${municipioNumber}"]`);
  1627.                 item.classList.remove('highlighted');
  1628.  
  1629.                 if (currentHighlight && currentHighlight !== currentBorder) {
  1630.                    map.removeLayer(currentHighlight);
  1631.                     currentHighlight = null;
  1632.                 }
  1633.  
  1634.                 // Se c'è un municipio selezionato, assicurati che rimanga visibile
  1635.                 if (selectedMunicipio && currentBorder) {
  1636.                    currentBorder.bringToFront();
  1637.                 }
  1638.             };
  1639.  
  1640.             // Funzione per gestire il click sulla legenda
  1641. window.handleLegendClick = async function(municipioNumber, event) {
  1642.     if (event) {
  1643.         event.stopPropagation();
  1644.     }
  1645.    
  1646.     // Rimuovi la parte che gestisce l'espansione e mantieni solo la logica di selezione del municipio
  1647.     const allItems = document.querySelectorAll('.legend-item');
  1648.     allItems.forEach(item => item.classList.remove('highlighted'));
  1649.    
  1650.     if (selectedMunicipio === municipioNumber) {
  1651.         if (currentBorder) {
  1652.             map.removeLayer(currentBorder);
  1653.             currentBorder = null;
  1654.         }
  1655.         if (municipioNumber === 10) {
  1656.             municipioLayers[10].setStyle({
  1657.                 color: municipalityColors[10],
  1658.                 weight: 3,
  1659.                 opacity: 0.65,
  1660.                 fillOpacity: 0
  1661.             });
  1662.         }
  1663.         selectedMunicipio = null;
  1664.         return;
  1665.     }
  1666.  
  1667.                 // Carica il GeoJSON se non è già stato caricato
  1668.                 if (!municipioLayers[municipioNumber]) {
  1669.                     try {
  1670.                         const response = await fetch(`municipio${municipioNumber}.geojson`);
  1671.                         const data = await response.json();
  1672.                         municipioLayers[municipioNumber] = L.geoJSON(data, {
  1673.                             style: {
  1674.                                 color: municipalityColors[municipioNumber],
  1675.                                 weight: 4,
  1676.                                 opacity: 0.9,
  1677.                                 fillOpacity: 0.2
  1678.                             }
  1679.                         });
  1680.                     } catch (error) {
  1681.                         console.error(`Error loading municipality ${municipioNumber}:`, error);
  1682.                         return;
  1683.                     }
  1684.                 }
  1685.  
  1686.                 // Rimuovi il bordo precedente se non è il municipio X
  1687.                 if (currentBorder && currentBorder !== municipioLayers[10]) {
  1688.                    map.removeLayer(currentBorder);
  1689.                 }
  1690.  
  1691.                 selectedMunicipio = municipioNumber;
  1692.                 const item = document.querySelector(`.legend-item[data-municipio="${municipioNumber}"]`);
  1693.                 if (item) {
  1694.                     item.classList.add('highlighted');
  1695.                 }
  1696.  
  1697.                 // Gestione speciale per il municipio X
  1698.                 if (municipioNumber === 10) {
  1699.                     municipioLayers[10].setStyle({
  1700.                         color: municipalityColors[10],
  1701.                         weight: 6,
  1702.                         opacity: 1,
  1703.                         fillOpacity: 0.3
  1704.                     }).bringToFront();
  1705.                     currentBorder = municipioLayers[10];
  1706.                 } else {
  1707.                     // Per altri municipi, aggiungi il nuovo bordo
  1708.                     currentBorder = municipioLayers[municipioNumber].addTo(map).bringToFront();
  1709.                 }
  1710.  
  1711.                 map.fitBounds(currentBorder.getBounds());
  1712.             };
  1713.  
  1714.             // Modifica della funzione handleLegendOut
  1715.             window.handleLegendOut = function(municipioNumber) {
  1716.                 if (municipioNumber === selectedMunicipio) return;
  1717.                
  1718.                 const item = document.querySelector(`.legend-item[data-municipio="${municipioNumber}"]`);
  1719.                 item.classList.remove('highlighted');
  1720.  
  1721.                 if (currentHighlight && currentHighlight !== currentBorder) {
  1722.                    map.removeLayer(currentHighlight);
  1723.                     currentHighlight = null;
  1724.                 }
  1725.  
  1726.                 // Se c'è un municipio selezionato, assicurati che rimanga visibile
  1727.                 if (selectedMunicipio && currentBorder) {
  1728.                    currentBorder.bringToFront();
  1729.                 }
  1730.  
  1731.                 // Assicurati che il municipio X rimanga sempre visibile
  1732.                 if (municipioLayers[10]) {
  1733.                     municipioLayers[10].bringToFront();
  1734.                 }
  1735.             };
  1736.         })
  1737.         .catch(error => console.error('Errore nel caricamento dei confini:', error));
  1738. });
  1739.  
  1740.  
  1741.  
  1742. function toggleRomeLibraries() {
  1743.     const button = document.getElementById('toggleRomeLibraries');
  1744.    
  1745.     if (romeLibrariesVisible) {
  1746.         // Fase di nascondimento
  1747.         removedRomeLibraries = []; // Resetta l'array
  1748.         map.eachLayer((layer) => {
  1749.             if (layer instanceof L.Marker) {
  1750.                 const libraryName = layer.options.title;
  1751.                 const isRomeLibrary = window.libraryData.libraries.some(lib => lib.name === libraryName);
  1752.                
  1753.                 if (isRomeLibrary) {
  1754.                     removedRomeLibraries.push(layer); // Memorizza il marker
  1755.                     layer.remove(); // Rimuovi dalla mappa
  1756.                 }
  1757.             }
  1758.         });
  1759.        
  1760.         button.textContent = 'Mostra Biblioteche di Roma';
  1761.         romeLibrariesVisible = false;
  1762.     } else {
  1763.         // Fase di ricomparsa
  1764.         removedRomeLibraries.forEach(marker => {
  1765.             marker.addTo(map); // Riaggiunge tutti i marker memorizzati
  1766.         });
  1767.        
  1768.         button.textContent = 'Nascondi Biblioteche di Roma';
  1769.         romeLibrariesVisible = true;
  1770.         removedRomeLibraries = []; // Resetta l'array dopo il ripristino
  1771.     }
  1772. }
  1773.  
  1774.  
  1775.  
  1776. function toggleFederatedLibraries() {
  1777.     const button = document.getElementById('toggleFederatedLibraries');
  1778.     if (federatedLibrariesVisible) {
  1779.         // Nascondi le biblioteche federate
  1780.         window.federatedMarkers.forEach(marker => {
  1781.             if (map.hasLayer(marker)) {
  1782.                 map.removeLayer(marker);
  1783.             }
  1784.         });
  1785.         button.textContent = 'Mostra Biblioteche Federate';
  1786.         federatedLibrariesVisible = false;
  1787.     } else {
  1788.         // Mostra le biblioteche federate
  1789.         window.federatedMarkers.forEach(marker => {
  1790.             if (!map.hasLayer(marker)) {
  1791.                 marker.addTo(map);
  1792.             }
  1793.         });
  1794.         button.textContent = 'Nascondi Biblioteche federate';
  1795.         federatedLibrariesVisible = true;
  1796.     }
  1797. }
  1798.  
  1799. function resetMap() {
  1800.     // Reset del campo di ricerca
  1801.     const searchInput = document.getElementById('start-point');
  1802.     searchInput.value = '';
  1803.    
  1804.     // Rimuovi il controllo del percorso se presente
  1805.     if (routingControl) {
  1806.         map.removeControl(routingControl);
  1807.         routingControl = null;
  1808.     }
  1809.    
  1810.     // Reset della vista della mappa
  1811.     const center = [41.732522, 12.271879];
  1812.     map.setView(center, 14);
  1813.    
  1814.     // Reset del municipio selezionato
  1815.     if (selectedMunicipio) {
  1816.         if (currentBorder) {
  1817.             map.removeLayer(currentBorder);
  1818.             currentBorder = null;
  1819.         }
  1820.         selectedMunicipio = null;
  1821.     }
  1822.    
  1823.     // Reset dell'evidenziazione nella legenda
  1824.     const allItems = document.querySelectorAll('.legend-item');
  1825.     allItems.forEach(item => item.classList.remove('highlighted'));
  1826.    
  1827.     // Nascondi i risultati della ricerca
  1828.     const searchResults = document.getElementById('search-results');
  1829.     if (searchResults) {
  1830.         searchResults.style.display = 'none';
  1831.     }
  1832.    
  1833.     // Reset del municipio X allo stile originale
  1834.     if (municipioLayers[10]) {
  1835.         municipioLayers[10].setStyle({
  1836.             color: municipalityColors[10],
  1837.             weight: 3,
  1838.             opacity: 0.65,
  1839.             fillOpacity: 0
  1840.         });
  1841.     }
  1842. }
  1843.  
  1844.        
  1845. function zoomToSystemLibraries(systemName) {
  1846.     // Verifica se ci sono marker delle biblioteche federate
  1847.     if (!window.federatedMarkers) {
  1848.         console.error('Marker delle biblioteche federate non trovati');
  1849.         return;
  1850.     }
  1851.  
  1852.     // Mappatura dei nomi dei sistemi alle stringhe di ricerca nei popup
  1853.     const systemMappings = {
  1854.         'CASTELLI ROMANI': 'CASTELLI ROMANI',
  1855.         'Ceretano-Sabatino': 'Ceretano-Sabatino',
  1856.         'Monti Prenestini': 'Monti Prenestini',
  1857.         'LUMSA': 'LUMSA',
  1858.         'Foro Italico': 'Foro Italico',
  1859.         'Link': 'Link Campus',
  1860.         'La Sapienza': 'La Sapienza',
  1861.         'Tor Vergata': 'Tor Vergata',
  1862.         'Roma Tre': 'Roma Tre'
  1863.     };
  1864.  
  1865.     const searchString = systemMappings[systemName] || systemName;
  1866.  
  1867.     // Filtra i marker del sistema bibliotecario specificato
  1868.     const systemMarkers = window.federatedMarkers.filter(marker => {
  1869.         const popupContent = marker.getPopup().getContent();
  1870.         return popupContent.includes(searchString);
  1871.     });
  1872.  
  1873.     if (systemMarkers.length === 0) {
  1874.         console.log('Nessun marker trovato per il sistema:', systemName);
  1875.         return;
  1876.     }
  1877.  
  1878.     // Crea un gruppo di layer per calcolare i bounds
  1879.     const group = L.featureGroup(systemMarkers);
  1880.    
  1881.     // Se le biblioteche federate non sono visibili, mostrale
  1882.     if (!federatedLibrariesVisible) {
  1883.         toggleFederatedLibraries();
  1884.     }
  1885.  
  1886.     // Rimuovi l'evidenziazione precedente se presente
  1887.     window.federatedMarkers.forEach(marker => {
  1888.         const icon = marker.getIcon();
  1889.         if (icon.options.className) {
  1890.             icon.options.className = '';
  1891.             marker.setIcon(icon);
  1892.         }
  1893.     });
  1894.  
  1895.     // Aggiungi tutti i marker del sistema alla mappa e evidenziali
  1896.     systemMarkers.forEach(marker => {
  1897.         if (!map.hasLayer(marker)) {
  1898.             marker.addTo(map);
  1899.         }
  1900.         // Evidenzia i marker del sistema selezionato
  1901.         const icon = marker.getIcon();
  1902.         icon.options.className = 'highlighted-marker';
  1903.         marker.setIcon(icon);
  1904.     });
  1905.  
  1906.     // Calcola i bounds e applica lo zoom con padding
  1907.     const bounds = group.getBounds();
  1908.     map.fitBounds(bounds, {
  1909.         padding: [50, 50], // Padding di 50 pixel su tutti i lati
  1910.         maxZoom: 13, // Limita lo zoom massimo per una vista migliore
  1911.         duration: 0.5 // Durata dell'animazione in secondi
  1912.     });
  1913.  
  1914.     // Mostra il numero di biblioteche trovate
  1915.     const count = systemMarkers.length;
  1916.     const message = `Trovate ${count} biblioteche del sistema ${systemName}`;
  1917.     console.log(message);
  1918. }
  1919.        
  1920.     </script>
  1921. </body>
  1922. </html>
  1923.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement