freealberta/map/app/public/js/map-search.js

197 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Map Search Module
* Handles address search functionality for the map
*/
import { map } from './map-manager.js';
import { openAddModal } from './location-manager.js';
export class MapSearch {
constructor() {
this.searchCache = new Map();
this.tempMarker = null;
}
/**
* Search for addresses using the geocoding API
* @param {string} query - The search query
* @returns {Promise<Array>} Array of search results
*/
async search(query) {
if (!query || query.trim().length < 2) {
return [];
}
const trimmedQuery = query.trim();
// Check cache first
if (this.searchCache.has(trimmedQuery)) {
return this.searchCache.get(trimmedQuery);
}
try {
const response = await fetch(`/api/geocode/search?query=${encodeURIComponent(trimmedQuery)}&limit=5`);
if (!response.ok) {
throw new Error(`Search failed: ${response.statusText}`);
}
const data = await response.json();
if (!data.success) {
throw new Error(data.error || 'Search failed');
}
const results = data.data || [];
// Cache the results
this.searchCache.set(trimmedQuery, results);
// Clean up cache if it gets too large
if (this.searchCache.size > 100) {
const firstKey = this.searchCache.keys().next().value;
this.searchCache.delete(firstKey);
}
return results;
} catch (error) {
console.error('Map search error:', error);
throw error;
}
}
/**
* Create HTML for a search result
* @param {Object} result - Search result object
* @returns {HTMLElement} Result element
*/
createResultElement(result) {
const resultEl = document.createElement('div');
resultEl.className = 'search-result-item search-result-map';
// Handle both coordinate formats for compatibility
const lat = result.coordinates?.lat || result.latitude || 0;
const lng = result.coordinates?.lng || result.longitude || 0;
// Debugging - log result structure if coordinates are missing
if (!lat && !lng) {
console.warn('Search result missing coordinates:', result);
}
resultEl.innerHTML = `
<div class="result-address">${result.formattedAddress || 'Unknown Address'}</div>
<div class="result-full-address">${result.fullAddress || ''}</div>
<div class="result-coordinates">${lat.toFixed(6)}, ${lng.toFixed(6)}</div>
`;
resultEl.addEventListener('click', () => {
this.selectResult(result);
});
return resultEl;
}
/**
* Handle selection of a search result
* @param {Object} result - Selected result
*/
selectResult(result) {
if (!map) {
console.error('Map not available');
return;
}
// Handle both coordinate formats for compatibility
const lat = parseFloat(result.coordinates?.lat || result.latitude || 0);
const lng = parseFloat(result.coordinates?.lng || result.longitude || 0);
if (isNaN(lat) || isNaN(lng)) {
console.error('Invalid coordinates in result:', result);
return;
}
// Pan and zoom to the location
map.setView([lat, lng], 16);
// Remove any existing temporary marker
this.clearTempMarker();
// Add a temporary marker
this.tempMarker = L.marker([lat, lng], {
icon: L.divIcon({
className: 'temp-search-marker',
html: '📍',
iconSize: [30, 30],
iconAnchor: [15, 30]
})
}).addTo(map);
// Create popup with add location option
const popupContent = `
<div class="search-result-popup">
<h3>${result.formattedAddress || 'Search Result'}</h3>
<p>${result.fullAddress || ''}</p>
<div class="popup-actions">
<button class="btn btn-success btn-sm" onclick="mapSearchInstance.openAddLocationModal(${lat}, ${lng})">
Add Location Here
</button>
<button class="btn btn-secondary btn-sm" onclick="mapSearchInstance.clearTempMarker()">
✕ Clear
</button>
</div>
</div>
`;
this.tempMarker.bindPopup(popupContent).openPopup();
// Auto-clear the marker after 30 seconds
setTimeout(() => {
this.clearTempMarker();
}, 30000);
}
/**
* Open the add location modal at specified coordinates
* @param {number} lat - Latitude
* @param {number} lng - Longitude
*/
openAddLocationModal(lat, lng) {
this.clearTempMarker();
if (typeof openAddModal === 'function') {
openAddModal(lat, lng);
} else {
// Fallback: trigger the add location button click
const addBtn = document.getElementById('add-location-btn');
if (addBtn) {
// Set a temporary flag for the coordinates
window.tempSearchCoordinates = { lat, lng };
addBtn.click();
}
}
}
/**
* Clear the temporary search marker
*/
clearTempMarker() {
if (this.tempMarker && map) {
map.removeLayer(this.tempMarker);
this.tempMarker = null;
}
}
/**
* Clear the search cache
*/
clearCache() {
this.searchCache.clear();
}
}
// Create a global instance for use in popup buttons
window.mapSearchInstance = new MapSearch();
export default window.mapSearchInstance;