You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

123 lines
5.0 KiB

/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { useService } from "@web/core/utils/hooks";
import { CharField, charField } from "@web/views/fields/char/char_field";
import { useRef, useState, onMounted } from "@odoo/owl";
import { registry } from "@web/core/registry";
/**
* GeoLocationMap class extends CharField to provide a map with geolocation features.
*/
export class GeoLocationMap extends CharField {
/**
* Sets up the component by initializing services, references, and state.
*/
setup() {
super.setup();
this.orm = useService('orm');
this.mapContainerRef = useRef('mapContainer');
this.state = useState({
latitude: '51.505',
longitude: '-0.09',
address: '',
currentMarker: null,
});
onMounted(() => this._initializeMap());
}
/**
* Initializes the Leaflet map and sets up event handlers.
* - Configures the map and tile layer.
* - Adds a click event listener to handle map clicks.
*/
async _initializeMap() {
const mapContainer = this.mapContainerRef.el;
const value = this.input.el.value
if (value){
await this.getLatLngFromAddress(value)
}
if (!mapContainer) {
console.error('Map container not found.');
return;
}
// Initialize the map
const map = L.map(mapContainer,{zoomControl: true,zoom:1,zoomAnimation:false,fadeAnimation:true,markerZoomAnimation:true}).setView([this.state.latitude, this.state.longitude], 13);
// Add tile layer
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 15 }).addTo(map);
this.state.currentMarker = L.marker([this.state.latitude, this.state.longitude])
.addTo(map)
.bindPopup('Selected Location')
.openPopup();
// Handle map clicks
map.on('click', async (event) => {
const { lat, lng } = event.latlng;
this.state.latitude = lat;
this.state.longitude = lng;
try {
const address = await this.getAddressFromLatLng(lat, lng);
await this.props.record.update({ [this.props.name]: address });
} catch (error) {
console.error('Error fetching address:', error);
}
if (this.state.currentMarker) {
map.removeLayer(this.state.currentMarker);
}
const newMarker = L.marker([lat, lng])
.addTo(map)
.bindPopup('Selected Location')
.openPopup();
this.state.currentMarker = newMarker;
});
}
/**
* Retrieves the address for the given latitude and longitude using the Nominatim API.
* @param {number} latitude - Latitude of the location.
* @param {number} longitude - Longitude of the location.
* @returns {Promise<string|null>} The formatted address or null if an error occurs.
*/
async getAddressFromLatLng(latitude, longitude) {
try {
const response = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=json`);
const data = await response.json();
return `${data.address.village || ''}, ${data.address.county || ''}, ${data.address.state || ''}, ${data.address.country || ''}`;
} catch (error) {
console.error('Error fetching address:', error);
return null;
}
}
/**
* Converts an address into latitude and longitude using Nominatim.
* @param {string} address - The address to geocode.
* @returns {Promise<{lat: number, lon: number} | null>} The latitude and longitude, or null if an error occurs.
*/
async getLatLngFromAddress(address) {
try {
const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address)}`);
const data = await response.json();
if (data.length > 0) {
this.state.latitude = parseFloat(data[0].lat);
this.state.longitude = parseFloat(data[0].lon);
}
return null;
} catch (error) {
console.error('Error fetching coordinates:', errlatitudeor);
return null;
}
}
/**
* Opens a Google Maps view for the current location.
* The location is determined by the state’s latitude and longitude.
*/
async _OpenMapview() {
const { longitude, latitude } = this.state;
if (latitude && longitude) {
window.open(`https://www.google.com/maps/search/?api=1&query=${latitude},${longitude}`, '_blank');
}
}
}
GeoLocationMap.template = 'GeoLocation';
export const geoLocationMap = {
...charField,
component: GeoLocationMap,
displayName: _t("GeoLocation Map Viewer"),
};
registry.category("fields").add("geolocation_map", geoLocationMap);