This module helps manage geofenced group locations and determines user proximity to these areas.
Each group location is represented by a circular area (defined by a center point and a radius). A user is considered "inside" a location if their coordinates fall within that circular boundary.
typescript
/**
* Calculates the distance between two coordinates in meters
*/
export const calculateDistance = (
lat1: number,
lon1: number,
lat2: number,
lon2: number
): number => {
const R = 6371e3; // Earth's radius in meters
const φ1 = (lat1 * Math.PI) / 180;
const φ2 = (lat2 * Math.PI) / 180;
const Δφ = ((lat2 - lat1) * Math.PI) / 180;
const Δλ = ((lon2 - lon1) * Math.PI) / 180;
const a =
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return distance;
};
/**
* Checks if a user is inside a location's circle
*/
export const isUserInsideLocation = (
userLat: number,
userLon: number,
locationLat: number,
locationLon: number,
radiusInMeters: number
): boolean => {
const distance = calculateDistance(userLat, userLon, locationLat, locationLon);
return distance <= radiusInMeters;
};
typescript
const handleCreateLocation = async () => {
if (!coordinates || !locationName || !radius) return;
setIsLoading(true);
try {
const { error } = await supabase.from("group_locations").insert({
group_id: groupId,
latitude: coordinates.latitude.toString(),
longitude: coordinates.longitude.toString(),
name: locationName,
radius: parseInt(radius),
});
if (error) throw error;
onClose();
} catch (error) {
console.error("Error creating location:", error);
} finally {
setIsLoading(false);
}
};
typescript
export interface GroupLocations {
id: string;
group_id: string;
latitude: string;
longitude: string;
updated_at: Date;
created_at: Date;
radius: number;
name: string;
}