1/** 2 * Search Map - Gestione mappa Google Maps per i risultati di ricerca 3 * Bestogoo 2025 4 * 5 * Features: 6 * - Marker numerati per ogni risultato 7 * - Info card al click sul marker 8 * - Pulsante "Cerca qui" quando si sposta la mappa 9 * - Accumulo marker quando si caricano più risultati 10 */ 11 12(function() { 13 'use strict'; 14 15 // Stato globale della mappa 16 window.SearchMap = { 17 map: null, 18 markers: [], 19 infoWindow: null, 20 results: [], 21 resultIds: new Set(), // Per evitare duplicati 22 currentView: 'list', 23 bounds: null, 24 selectedMarker: null, 25 searchHereButton: null, 26 lastSearchBounds: null, 27 isUserInteraction: false, 28 markerIndex: 0, // Contatore globale per i marker 29 lastSearchedCity: null, // Ultima città cercata per rilevare cambio città 30 geocodeCache: {} // Cache per risultati geocoding (risparmio API calls) 31 }; 32 33 /** 34 * Inizializza la mappa Google Maps 35 */ 36 window.initSearchMap = function(centerLat, centerLng) { 37 const mapContainer = document.getElementById('search-map'); 38 if (!mapContainer) return; 39 40 // Valida coordinate centro - default Roma se non valide 41 let lat = parseFloat(centerLat); 42 let lng = parseFloat(centerLng); 43 44 if (isNaN(lat) || isNaN(lng) || lat === 0 || lng === 0) { 45 lat = 41.9028; // Roma 46 lng = 12.4964; 47 } 48 49 const center = { lat: lat, lng: lng }; 50 51 SearchMap.map = new google.maps.Map(mapContainer, { 52 zoom: 12, 53 center: center, 54 mapTypeControl: false, 55 streetViewControl: false, 56 fullscreenControl: true, 57 zoomControl: true, 58 styles: [ 59 { 60 featureType: "poi", 61 elementType: "labels", 62 stylers: [{ visibility: "off" }] 63 } 64 ] 65 }); 66 67 SearchMap.bounds = new google.maps.LatLngBounds(); 68 SearchMap.infoWindow = new google.maps.InfoWindow(); 69 70 // Crea il pulsante "Cerca qui" 71 createSearchHereButton(); 72 73 // Listener per movimento mappa 74 SearchMap.map.addListener('dragend', function() { 75 SearchMap.isUserInteraction = true; 76 showSearchHereButton(); 77 }); 78 79 SearchMap.map.addListener('zoom_changed', function() { 80 if (SearchMap.isUserInteraction) { 81 showSearchHereButton(); 82 } 83 }); 84 85 // Imposta flag interazione utente 86 SearchMap.map.addListener('dragstart', function() { 87 SearchMap.isUserInteraction = true; 88 }); 89 90 console.log('[SearchMap] Mappa inizializzata con centro:', lat, lng); 91 }; 92 93 /** 94 * Crea il pulsante "Cerca qui" 95 */ 96 function createSearchHereButton() { 97 const button = document.createElement('button'); 98 button.id = 'search-here-btn'; 99 button.className = 'search-here-button'; 100 button.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg> Cerca qui'; 101 button.style.display = 'none'; 102 button.onclick = searchInCurrentArea; 103 104 const mapContainer = document.getElementById('search-map-container'); 105 if (mapContainer) { 106 mapContainer.appendChild(button); 107 } 108 109 SearchMap.searchHereButton = button; 110 } 111 112 /** 113 * Mostra il pulsante "Cerca qui" 114 */ 115 function showSearchHereButton() { 116 if (!SearchMap.searchHereButton) return; 117 118 // Mostra solo se abbiamo spostato significativamente la mappa 119 const currentBounds = SearchMap.map.getBounds(); 120 if (!currentBounds) return; 121 122 if (SearchMap.lastSearchBounds) { 123 const lastCenter = SearchMap.lastSearchBounds.getCenter(); 124 const currentCenter = currentBounds.getCenter(); 125 126 // Calcola la distanza tra i centri 127 const distance = google.maps.geometry ? 128 google.maps.geometry.spherical.computeDistanceBetween(lastCenter, currentCenter) : 129 Math.sqrt(Math.pow(lastCenter.lat() - currentCenter.lat(), 2) + Math.pow(lastCenter.lng() - currentCenter.lng(), 2)) * 111000; 130 131 // Mostra pulsante solo se spostamento > 500m 132 if (distance > 500) { 133 SearchMap.searchHereButton.style.display = 'flex'; 134 } 135 } else { 136 SearchMap.searchHereButton.style.display = 'flex'; 137 } 138 } 139 140 /** 141 * Nasconde il pulsante "Cerca qui" 142 */ 143 function hideSearchHereButton() { 144 if (SearchMap.searchHereButton) { 145 SearchMap.searchHereButton.style.display = 'none'; 146 } 147 } 148 149 /** 150 * Cerca attività nell'area corrente della mappa 151 */ 152 var isSearchingFromMap = false; // Flag per prevenire chiamate duplicate 153 154 function searchInCurrentArea() { 155 // Previeni chiamate duplicate 156 if (isSearchingFromMap) { 157 console.log('[SearchMap] Ricerca già in corso, skip'); 158 return; 159 } 160 161 const bounds = SearchMap.map.getBounds(); 162 if (!bounds) return; 163 164 const center = bounds.getCenter(); 165 const ne = bounds.getNorthEast(); 166 167 // Calcola raggio approssimativo in km 168 const radius = google.maps.geometry ? 169 google.maps.geometry.spherical.computeDistanceBetween(center, ne) / 1000 : 170 Math.sqrt(Math.pow(center.lat() - ne.lat(), 2) + Math.pow(center.lng() - ne.lng(), 2)) * 111; 171 172 console.log('[SearchMap] Cerca nell\'area:', center.lat(), center.lng(), 'raggio:', radius.toFixed(2), 'km'); 173 174 // Imposta flag per prevenire duplicati 175 isSearchingFromMap = true; 176 177 // NON pulisci i marker qui - lascia che sia updateSearchMap a gestirli 178 // clearSearchMarkers(); 179 // SearchMap.markerIndex = 0; 180 181 // Salva bounds correnti 182 SearchMap.lastSearchBounds = bounds; 183 hideSearchHereButton(); 184 185 // Chiudi info card 186 closeMapInfoCard(); 187 188 // Crea chiave cache basata su coordinate arrotondate (precisione ~1km) 189 const cacheKey = Math.round(center.lat() * 100) + '_' + Math.round(center.lng() * 100); 190 191 // Funzione per eseguire la ricerca con il nome città 192 function executeSearch(cityName) { 193 console.log('[SearchMap] Città trovata:', cityName); 194 195 // Aggiorna il campo di ricerca location 196 const locInput = document.getElementById('loc'); 197 if (locInput && cityName) { 198 locInput.value = cityName; 199 200 // Chiama direttamente geocodeAndFind per evitare doppia chiamata da findNear 201 if (typeof geocodeAndFind === 'function') { 202 geocodeAndFind(cityName, 203 null, 204 typeof subcategorySelected !== 'undefined' ? subcategorySelected : null, 205 typeof searchType !== 'undefined' ? searchType : null); 206 } 207 } 208 209 // Reset flag dopo un breve delay per permettere alla ricerca di completarsi 210 setTimeout(function() { 211 isSearchingFromMap = false;
212 }, 2000); 213 } 214 215 // Controlla cache prima di chiamare API 216 if (SearchMap.geocodeCache[cacheKey]) { 217 console.log('[SearchMap] Usando città da cache:', SearchMap.geocodeCache[cacheKey]); 218 executeSearch(SearchMap.geocodeCache[cacheKey]); 219 return; 220 } 221 222 // Usa reverse geocoding per ottenere il nome della località 223 const geocoder = new google.maps.Geocoder(); 224 geocoder.geocode({ location: { lat: center.lat(), lng: center.lng() } }, function(results, status) { 225 if (status === 'OK' && results && results.length > 0) { 226 // Cerca il nome della città nei risultati 227 let cityName = ''; 228 for (let i = 0; i < results.length; i++) { 229 const result = results[i]; 230 // Cerca locality (città ) o administrative_area_level_3 (comune) 231 for (let j = 0; j < result.address_components.length; j++) { 232 const component = result.address_components[j]; 233 if (component.types.includes('locality') || 234 component.types.includes('administrative_area_level_3')) { 235 cityName = component.long_name; 236 break; 237 } 238 } 239 if (cityName) break; 240 } 241 242 // Se non trova città , usa l'indirizzo formattato 243 if (!cityName && results[0]) { 244 cityName = results[0].formatted_address; 245 } 246 247 // Salva in cache 248 SearchMap.geocodeCache[cacheKey] = cityName; 249 250 executeSearch(cityName); 251 } else { 252 console.warn('[SearchMap] Geocoding fallito:', status); 253 alert('Non è stato possibile determinare la località . Prova a spostarti su una zona diversa.'); 254 isSearchingFromMap = false; 255 } 256 }); 257 } 258 259 /** 260 * Aggiunge i marker sulla mappa dai risultati della ricerca 261 * @param {Array} results - Risultati da aggiungere 262 * @param {boolean} append - Se true, aggiunge ai marker esistenti 263 */ 264 window.addSearchMarkers = function(results, append) { 265 if (!SearchMap.map) { 266 console.warn('[SearchMap] Mappa non inizializzata'); 267 return; 268 } 269 270 // Se non append, pulisci i marker esistenti 271 if (!append) { 272 clearSearchMarkers(); 273 SearchMap.markerIndex = 0; 274 } 275 276 let addedCount = 0; 277 278 results.forEach(function(result) { 279 // Crea ID univoco per evitare duplicati 280 const resultId = result.place_id || result.id || (result.name + '_' + (result.geometry?.location?.lat || '')); 281 282 // Salta se già presente 283 if (SearchMap.resultIds.has(resultId)) { 284 return; 285 } 286 287 // Verifica che ci siano coordinate valide 288 if (!result.geometry || !result.geometry.location) { 289 console.log('[SearchMap] Risultato senza coordinate (saltato):', decodeText(result.name)); 290 return; 291 } 292 293 const lat = parseFloat(result.geometry.location.lat); 294 const lng = parseFloat(result.geometry.location.lng); 295 296 // Salta se coordinate non valide o zero 297 if (isNaN(lat) || isNaN(lng) || (lat === 0 && lng === 0)) { 298 console.log('[SearchMap] Coordinate non valide (saltato):', decodeText(result.name)); 299 return; 300 } 301 302 const position = { lat: lat, lng: lng }; 303 SearchMap.markerIndex++; 304 const markerNumber = SearchMap.markerIndex; 305 306 // Crea marker personalizzato con numero 307 const marker = new google.maps.Marker({ 308 position: position, 309 map: SearchMap.map, 310 title: decodeText(result.name), 311 label: { 312 text: String(markerNumber),
313 color: '#FFFFFF', 314 fontSize: '12px', 315 fontWeight: 'bold' 316 }, 317 icon: { 318 path: google.maps.SymbolPath.CIRCLE, 319 scale: 18, 320 fillColor: '#70c542', 321 fillOpacity: 1, 322 strokeColor: '#FFFFFF', 323 strokeWeight: 2 324 }, 325 zIndex: 1000 - markerNumber 326 }); 327 328 // Salva dati nel marker 329 marker.resultData = result; 330 marker.resultIndex = markerNumber - 1; 331 marker.resultId = resultId; 332 333 // Click handler 334 marker.addListener('click', function() { 335 selectMarker(marker); 336 showMapInfoCard(result, marker.resultIndex); 337 }); 338 339 // Hover effect 340 marker.addListener('mouseover', function() { 341 highlightListItem(marker.resultIndex, true); 342 }); 343 344 marker.addListener('mouseout', function() { 345 highlightListItem(marker.resultIndex, false); 346 }); 347 348 SearchMap.markers.push(marker); 349 SearchMap.results.push(result); 350 SearchMap.resultIds.add(resultId); 351 SearchMap.bounds.extend(position); 352 addedCount++; 353 }); 354 355 console.log('[SearchMap] Aggiunti ' + addedCount + ' nuovi marker (totale: ' + SearchMap.markers.length + ')'); 356 }; 357 358 /** 359 * Pulisce tutti i marker dalla mappa 360 */ 361 window.clearSearchMarkers = function() { 362 SearchMap.markers.forEach(function(marker) { 363 marker.setMap(null); 364 }); 365 SearchMap.markers = []; 366 SearchMap.results = []; 367 SearchMap.resultIds.clear(); 368 SearchMap.bounds = new google.maps.LatLngBounds(); 369 }; 370 371 /** 372 * Seleziona un marker e lo evidenzia 373 */ 374 function selectMarker(marker) { 375 // Reset marker precedente 376 if (SearchMap.selectedMarker && SearchMap.selectedMarker !== marker) { 377 const prevIndex = SearchMap.selectedMarker.resultIndex + 1; 378 SearchMap.selectedMarker.setIcon({ 379 path: google.maps.SymbolPath.CIRCLE, 380 scale: 18, 381 fillColor: '#70c542', 382 fillOpacity: 1, 383 strokeColor: '#FFFFFF', 384 strokeWeight: 2 385 }); 386 SearchMap.selectedMarker.setLabel({ 387 text: String(prevIndex),
388 color: '#FFFFFF', 389 fontSize: '12px', 390 fontWeight: 'bold' 391 }); 392 } 393 394 // Evidenzia nuovo marker 395 marker.setIcon({ 396 path: google.maps.SymbolPath.CIRCLE, 397 scale: 22, 398 fillColor: '#ff6b35', 399 fillOpacity: 1, 400 strokeColor: '#FFFFFF', 401 strokeWeight: 3 402 }); 403 404 SearchMap.selectedMarker = marker; 405 } 406 407 /** 408 * Mostra la card informativa al click sul marker 409 */ 410 window.showMapInfoCard = function(result, index) { 411 const card = document.getElementById('map-info-card'); 412 const content = card.querySelector('.map-info-card-content'); 413 414 if (!card || !content) return; 415 416 // Costruisci URL della pagina di dettaglio 417 const detailUrl = buildDetailUrl(result); 418 419 // Decodifica nome (potrebbe essere URL-encoded dal DB) 420 const name = decodeText(result.name); 421 422 // Immagine 423 let imageHtml = ''; 424 let photoUrl = null; 425 426 if (result.photos && result.photos.length > 0) { 427 const photo = result.photos[0]; 428 // Caso 1: URL diretto dal DB 429 if (photo.url) { 430 let imgUrl = photo.url; 431 // Fix percorsi relativi 432 if (imgUrl.includes('../bestogoo/')) { 433 imgUrl = imgUrl.replace('../', 'https://www.bestogoo.com/'); 434 } else if (imgUrl.includes('home/')) { 435 imgUrl = imgUrl.replace('home/', '').replace('/web/htdocs/', 'https://'); 436 } else if (imgUrl.startsWith('../')) { 437 imgUrl = imgUrl.replace('../', 'https://www.bestogoo.com/bestogoo/'); 438 } 439 photoUrl = imgUrl; 440 } 441 // Caso 2: Metodo getUrl() di Google Places 442 else if (typeof photo.getUrl === 'function') { 443 photoUrl = photo.getUrl({ maxWidth: 400 }); 444 } 445 // Caso 3: URL diretto come stringa 446 else if (typeof photo === 'string') { 447 photoUrl = photo; 448 } 449 } 450 451 if (photoUrl) { 452 imageHtml = '<img src="' + photoUrl + '" alt="' + escapeHtml(name) + '" class="map-info-card-image" onerror="this.parentElement.innerHTML=\'<div class=map-info-card-image-placeholder><svg width=48 height=48 viewBox=0_0_24_24 fill=none stroke=#ccc stroke-width=1.5><rect x=3 y=3 width=18 height=18 rx=2 ry=2></rect></svg></div>\'">'; 453 } else { 454 imageHtml = '<div class="map-info-card-image-placeholder"><svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#ccc" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg></div>'; 455 } 456 457 // Rating 458 let ratingHtml = ''; 459 if (result.rating) { 460 const stars = Math.round(result.rating); 461 ratingHtml = '<div class="map-info-card-rating">'; 462 for (let i = 0; i < 5; i++) { 463 ratingHtml += i < stars ? 464 '<span class="star filled">★</span>' : 465 '<span class="star">☆</span>'; 466 } 467 ratingHtml += ' <span class="rating-value">' + result.rating.toFixed(1) + '</span>'; 468 ratingHtml += '</div>'; 469 } 470 471 // Indirizzo completo: prima l'indirizzo specifico, poi città /regione 472 let addressParts = []; 473 if (result.address) { 474 addressParts.push(decodeText(result.address)); 475 } 476 if (result.formatted_address) { 477 addressParts.push(decodeText(result.formatted_address)); 478 } else if (result.city) { 479 addressParts.push(decodeText(result.city)); 480 } 481 // Rimuovi duplicati e unisci 482 const address = [...new Set(addressParts)].join(' - '); 483 484 content.innerHTML = 485 '<a href="' + detailUrl + '" class="map-info-card-link">' + 486 imageHtml + 487 '<div class="map-info-card-body">' + 488 '<div class="map-info-card-position">' + (index + 1) + '</div>' + 489 '<h3 class="map-info-card-title">' + escapeHtml(name) + '</h3>' + 490 ratingHtml + 491 (address ? '<p class="map-info-card-address">' + escapeHtml(address) + '</p>' : '') + 492 '</div>' + 493 '<div class="map-info-card-arrow">' + 494 '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"></polyline></svg>
494' + 495 '</div>' + 496 '</a>'; 497 498 card.style.display = 'block'; 499 500 // Animazione entrata 501 card.classList.remove('visible'); 502 setTimeout(function() { 503 card.classList.add('visible'); 504 }, 10); 505 }; 506 507 /** 508 * Chiude la card informativa 509 */ 510 window.closeMapInfoCard = function() { 511 const card = document.getElementById('map-info-card'); 512 if (card) { 513 card.classList.remove('visible'); 514 setTimeout(function() { 515 card.style.display = 'none'; 516 }, 300); 517 } 518 519 // Deseleziona marker 520 if (SearchMap.selectedMarker) { 521 const prevIndex = SearchMap.selectedMarker.resultIndex + 1; 522 SearchMap.selectedMarker.setIcon({ 523 path: google.maps.SymbolPath.CIRCLE, 524 scale: 18, 525 fillColor: '#70c542', 526 fillOpacity: 1, 527 strokeColor: '#FFFFFF', 528 strokeWeight: 2 529 }); 530 SearchMap.selectedMarker.setLabel({ 531 text: String(prevIndex),
532 color: '#FFFFFF', 533 fontSize: '12px', 534 fontWeight: 'bold' 535 }); 536 SearchMap.selectedMarker = null; 537 } 538 }; 539 540 /** 541 * Costruisce URL per la pagina di dettaglio 542 */ 543 function buildDetailUrl(result) { 544 // Se ha un link specifico, usalo 545 if (result.link) { 546 return result.link; 547 } 548 549 // Altrimenti costruisci l'URL standard 550 const baseUrl = window.location.origin; 551 const lang = document.documentElement.lang || 'it'; 552 553 if (result.linkitem) { 554 return baseUrl + '/' + lang + '/' + result.linkitem; 555 } 556 557 // Usa place_id con parametro activity (formato Bestogoo) 558 if (result.place_id) { 559 return baseUrl + '/detail.php?activity=' + encodeURIComponent(result.place_id); 560 } 561 562 // Fallback: usa id 563 if (result.id) { 564 return baseUrl + '/detail.php?id=' + encodeURIComponent(result.id); 565 } 566 567 return '#'; 568 } 569 570 /** 571 * Evidenzia elemento nella lista quando si passa sopra il marker 572 */ 573 function highlightListItem(index, highlight) { 574 const listItems = document.querySelectorAll('#box006b .box007'); 575 if (listItems[index]) { 576 if (highlight) { 577 listItems[index].classList.add('map-hover-highlight'); 578 } else { 579 listItems[index].classList.remove('map-hover-highlight'); 580 } 581 } 582 } 583 584 /** 585 * Switch tra vista lista e mappa 586 */ 587 window.switchSearchView = function(view) { 588 const listContainer = document.getElementById('box006b'); 589 const mapContainer = document.getElementById('search-map-container'); 590 const tabs = document.querySelectorAll('.search-view-tab'); 591 const loaderPager = document.getElementById('loader_pager'); 592 593 if (!listContainer || !mapContainer) return; 594 595 SearchMap.currentView = view; 596 597 // Aggiorna tabs
598 tabs.forEach(function(tab) { 599 if (tab.dataset.view === view) { 600 tab.classList.add('active'); 601 } else { 602 tab.classList.remove('active'); 603 } 604 }); 605 606 if (view === 'map') { 607 // Aggiungi classe al body per stili specifici mappa 608 document.body.classList.add('search-map-active'); 609 listContainer.style.display = 'none'; 610 mapContainer.style.display = 'block'; 611 if (loaderPager) loaderPager.style.display = 'none'; 612 613 // Inizializza mappa se non esiste 614 if (!SearchMap.map) { 615 initSearchMap(); 616 } 617 618 // Trigger resize per fix rendering 619 setTimeout(function() { 620 google.maps.event.trigger(SearchMap.map, 'resize'); 621 if (SearchMap.markers.length > 0) { 622 SearchMap.map.fitBounds(SearchMap.bounds); 623 // Limita lo zoom massimo a 15 (con pochi marker fitBounds zooma troppo) 624 if (SearchMap.map.getZoom() > 15) { 625 SearchMap.map.setZoom(15); 626 } 627 // Salva bounds correnti come riferimento 628 SearchMap.lastSearchBounds = SearchMap.map.getBounds(); 629 } else if (SearchMap.lastSearchedCity) { 630 // Nessun marker ma abbiamo una città cercata - centra su quella 631 centerMapOnCity(null, null, SearchMap.lastSearchedCity); 632 } 633 }, 100); 634 635 } else { 636 // Rimuovi classe dal body quando si torna alla lista 637 document.body.classList.remove('search-map-active'); 638 listContainer.style.display = 'block'; 639 mapContainer.style.display = 'none'; 640 if (loaderPager) loaderPager.style.display = 'block'; 641 closeMapInfoCard(); 642 hideSearchHereButton(); 643 } 644 645 console.log('[SearchMap] Vista cambiata a: ' + view); 646 }; 647 648 /** 649 * Centra la mappa su un marker specifico (chiamato dalla lista) 650 */ 651 window.focusMapMarker = function(index) { 652 if (SearchMap.currentView !== 'map') { 653 switchSearchView('map'); 654 } 655 656 setTimeout(function() { 657 if (SearchMap.markers[index]) { 658 const marker = SearchMap.markers[index]; 659 selectMarker(marker); 660 showMapInfoCard(marker.resultData, index); 661 SearchMap.map.panTo(marker.getPosition()); 662 SearchMap.map.setZoom(15); 663 } 664 }, 150); 665 }; 666 667 /** 668 * Utility: escape HTML 669 */ 670 function escapeHtml(text) { 671 if (!text) return ''; 672 const div = document.createElement('div'); 673 div.textContent = text; 674 return div.innerHTML; 675 } 676 677 /** 678 * Utility: decodifica testo URL-encoded 679 */ 680 function decodeText(text) { 681 if (!text) return ''; 682 try { 683 // Prima prova decodeURIComponent standard 684 let decoded = decodeURIComponent(text); 685 // Se contiene ancora % potrebbe essere doppia codifica 686 if (decoded.includes('%')) { 687 try { 688 decoded = decodeURIComponent(decoded); 689 } catch (e2) { 690 // Ignora errori di doppia decodifica 691 } 692 } 693 return decoded; 694 } catch (e) { 695 // Fallback: prova unescape (deprecato ma gestisce più casi) 696 try { 697 return unescape(text); 698 } catch (e2) { 699 return text; 700 } 701 } 702 } 703 704 /** 705 * Aggiorna la mappa quando i risultati cambiano 706 * Chiamato dal callback in core.js 707 * @param {Array} results - Nuovi risultati 708 * @param {number} centerLat - Latitudine centro 709 * @param {number} centerLng - Longitudine centro 710 * @param {boolean} append - Se true, aggiunge ai marker esistenti (default: false) 711 * @param {string} searchedCity - Nome della città cercata (per geocoding fallback) 712 */ 713 window.updateSearchMap = function(results, centerLat, centerLng, append, searchedCity) { 714 // Rileva se la città è cambiata 715 var cityChanged = !append && searchedCity && searchedCity !== SearchMap.lastSearchedCity; 716 if (searchedCity && !append) { 717 SearchMap.lastSearchedCity = searchedCity; 718 } 719 720 // Inizializza mappa se necessario 721 if (!SearchMap.map) { 722 initSearchMap(centerLat, centerLng); 723 } 724 725 // Disabilita temporaneamente il flag interazione utente 726 SearchMap.isUserInteraction = false;
727 728 // Aggiungi marker (append = true per aggiungere, false per sostituire) 729 addSearchMarkers(results, append === true); 730 731 // Se siamo in vista mappa, aggiorna la visualizzazione 732 if (SearchMap.currentView === 'map') { 733 setTimeout(function() { 734 google.maps.event.trigger(SearchMap.map, 'resize'); 735 if (SearchMap.markers.length > 0 && !append) { 736 SearchMap.map.fitBounds(SearchMap.bounds); 737 // Limita lo zoom massimo a 15 (con pochi marker fitBounds zooma troppo) 738 if (SearchMap.map.getZoom() > 15) { 739 SearchMap.map.setZoom(15); 740 } 741 SearchMap.lastSearchBounds = SearchMap.map.getBounds(); 742 } else if (SearchMap.markers.length === 0 && !append) { 743 // Nessun marker - centra sulla città cercata usando geocoding 744 centerMapOnCity(centerLat, centerLng, searchedCity); 745 } else if (cityChanged && SearchMap.markers.length === 0) { 746 // Città cambiata ma nessun marker con coordinate valide 747 centerMapOnCity(centerLat, centerLng, searchedCity); 748 } 749 // Nascondi pulsante cerca qui dopo aggiornamento 750 hideSearchHereButton(); 751 }, 100); 752 } else { 753 // Anche se non siamo in vista mappa, prepara il centro corretto per quando si switcha 754 if (cityChanged || (SearchMap.markers.length === 0 && !append && searchedCity)) { 755 centerMapOnCity(centerLat, centerLng, searchedCity); 756 } 757 } 758 }; 759 760 /** 761 * Centra la mappa sulla città cercata usando geocoding 762 * @param {number} centerLat - Latitudine fornita (può essere null) 763 * @param {number} centerLng - Longitudine fornita (può essere null) 764 * @param {string} cityName - Nome della città 765 */ 766 function centerMapOnCity(centerLat, centerLng, cityName) { 767 // Verifica che la mappa sia inizializzata 768 if (!SearchMap.map) { 769 console.log('[SearchMap] centerMapOnCity: mappa non ancora inizializzata, skip'); 770 return; 771 } 772 773 // Prima prova le coordinate fornite 774 var lat = parseFloat(centerLat); 775 var lng = parseFloat(centerLng); 776 777 if (!isNaN(lat) && !isNaN(lng) && lat !== 0 && lng !== 0) { 778 SearchMap.map.setCenter({ lat: lat, lng: lng }); 779 SearchMap.map.setZoom(12); 780 console.log('[SearchMap] Centrato su coordinate fornite:', lat, lng); 781 return; 782 } 783 784 // Fallback: usa Geocoding API per la città (con cache) 785 if (cityName && typeof google !== 'undefined' && google.maps && google.maps.Geocoder) { 786 // Crea chiave cache per la città 787 var cityCacheKey = 'city_' + cityName.toLowerCase().replace(/[^a-z0-9]/g, '_'); 788 789 // Controlla cache 790 if (SearchMap.geocodeCache[cityCacheKey]) { 791 var cached = SearchMap.geocodeCache[cityCacheKey]; 792 SearchMap.map.setCenter({ lat: cached.lat, lng: cached.lng }); 793 SearchMap.map.setZoom(12); 794 console.log('[SearchMap] Centrato su città (da cache):', cityName, cached.lat, cached.lng); 795 return; 796 } 797 798 var geocoder = new google.maps.Geocoder(); 799 geocoder.geocode({ address: cityName + ', Italia' }, function(results, status) { 800 if (status === 'OK' && results && results[0]) { 801 var location = results[0].geometry.location; 802 var lat = typeof location.lat === 'function' ? location.lat() : location.lat; 803 var lng = typeof location.lng === 'function' ? location.lng() : location.lng; 804 805 // Salva in cache 806 SearchMap.geocodeCache[cityCacheKey] = { lat: lat, lng: lng }; 807 808 SearchMap.map.setCenter({ lat: lat, lng: lng }); 809 SearchMap.map.setZoom(12); 810 console.log('[SearchMap] Centrato su città (geocoding):', cityName, lat, lng); 811 } else { 812 console.warn('[SearchMap] Geocoding fallito per:', cityName, status); 813 } 814 }); 815 } 816 } 817 818 /** 819 * Esponi funzione per aggiungere marker senza sostituire 820 */ 821 window.appendSearchMarkers = function(results) { 822 updateSearchMap(results, null, null, true); 823 }; 824 825})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.