UNPKG

expo-osm-sdk

Version:

OpenStreetMap component for React Native with Expo

280 lines 9.76 kB
"use strict"; /** * Geofencing Utility Functions * * Core algorithms for geofence detection: * - Distance calculations (Haversine formula) * - Point-in-polygon detection (Ray casting algorithm) * - Circle boundary checks */ Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateDistance = calculateDistance; exports.isPointInCircle = isPointInCircle; exports.isPointInPolygon = isPointInPolygon; exports.isPointInGeofence = isPointInGeofence; exports.distanceToGeofence = distanceToGeofence; exports.validateGeofence = validateGeofence; exports.getGeofenceCenter = getGeofenceCenter; exports.doGeofencesOverlap = doGeofencesOverlap; /** * Calculate distance between two coordinates using Haversine formula * @param point1 First coordinate * @param point2 Second coordinate * @returns Distance in meters */ function calculateDistance(point1, point2) { const R = 6371e3; // Earth radius in meters const φ1 = (point1.latitude * Math.PI) / 180; const φ2 = (point2.latitude * Math.PI) / 180; const Δφ = ((point2.latitude - point1.latitude) * Math.PI) / 180; const Δλ = ((point2.longitude - point1.longitude) * Math.PI) / 180; const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + Math.cos1) * Math.cos2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; // Distance in meters } /** * Check if a point is inside a circle geofence * @param point Point to check * @param geofence Circle geofence * @returns true if point is inside the circle */ function isPointInCircle(point, geofence) { const distance = calculateDistance(point, geofence.center); return distance <= geofence.radius; } /** * Check if a point is inside a polygon geofence using ray casting algorithm * @param point Point to check * @param geofence Polygon geofence * @returns true if point is inside the polygon */ function isPointInPolygon(point, geofence) { const coordinates = geofence.coordinates; // Need at least 3 points for a polygon if (coordinates.length < 3) { console.warn(`Polygon geofence ${geofence.id} has less than 3 points`); return false; } let inside = false; const x = point.longitude; const y = point.latitude; for (let i = 0, j = coordinates.length - 1; i < coordinates.length; j = i++) { const coordI = coordinates[i]; const coordJ = coordinates[j]; if (!coordI || !coordJ) continue; const xi = coordI.longitude; const yi = coordI.latitude; const xj = coordJ.longitude; const yj = coordJ.latitude; const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) { inside = !inside; } } return inside; } /** * Check if a point is inside any geofence * @param point Point to check * @param geofence Geofence (circle or polygon) * @returns true if point is inside the geofence */ function isPointInGeofence(point, geofence) { if (geofence.type === 'circle') { return isPointInCircle(point, geofence); } else if (geofence.type === 'polygon') { return isPointInPolygon(point, geofence); } return false; } /** * Calculate distance from point to geofence boundary * @param point Point to check * @param geofence Geofence * @returns Distance in meters (positive if outside, negative if inside) */ function distanceToGeofence(point, geofence) { if (geofence.type === 'circle') { const distance = calculateDistance(point, geofence.center); return distance - geofence.radius; } else if (geofence.type === 'polygon') { // For polygon, calculate minimum distance to any edge return distanceToPolygonEdge(point, geofence.coordinates); } return 0; } /** * Calculate minimum distance from point to polygon edge * @param point Point to check * @param coordinates Polygon coordinates * @returns Minimum distance in meters */ function distanceToPolygonEdge(point, coordinates) { let minDistance = Infinity; for (let i = 0; i < coordinates.length; i++) { const start = coordinates[i]; const end = coordinates[(i + 1) % coordinates.length]; if (!start || !end) continue; const distance = distanceToLineSegment(point, start, end); minDistance = Math.min(minDistance, distance); } return minDistance; } /** * Calculate distance from point to line segment * @param point Point to check * @param lineStart Start of line segment * @param lineEnd End of line segment * @returns Distance in meters */ function distanceToLineSegment(point, lineStart, lineEnd) { // Convert coordinates to approximate Cartesian (good enough for small distances) const A = point.latitude - lineStart.latitude; const B = point.longitude - lineStart.longitude; const C = lineEnd.latitude - lineStart.latitude; const D = lineEnd.longitude - lineStart.longitude; const dot = A * C + B * D; const lenSq = C * C + D * D; if (lenSq === 0) { // Line segment is actually a point return calculateDistance(point, lineStart); } const param = dot / lenSq; let closestPoint; if (param < 0) { closestPoint = lineStart; } else if (param > 1) { closestPoint = lineEnd; } else { closestPoint = { latitude: lineStart.latitude + param * C, longitude: lineStart.longitude + param * D, }; } return calculateDistance(point, closestPoint); } /** * Validate geofence configuration * @param geofence Geofence to validate * @returns true if valid, false otherwise */ function validateGeofence(geofence) { // Check ID if (!geofence || !geofence.id || typeof geofence.id !== 'string') { console.warn('Geofence must have a valid ID'); return false; } // Check type const geofenceType = geofence.type; if (geofenceType !== 'circle' && geofenceType !== 'polygon') { console.warn(`Invalid geofence type: ${geofenceType}`); return false; } // Type-specific validation if (geofenceType === 'circle') { const circleGeofence = geofence; if (!circleGeofence.center || typeof circleGeofence.center.latitude !== 'number' || typeof circleGeofence.center.longitude !== 'number') { console.warn('Circle geofence must have a valid center coordinate'); return false; } if (typeof circleGeofence.radius !== 'number' || circleGeofence.radius <= 0) { console.warn('Circle geofence must have a positive radius'); return false; } } else if (geofenceType === 'polygon') { const polygonGeofence = geofence; if (!Array.isArray(polygonGeofence.coordinates) || polygonGeofence.coordinates.length < 3) { console.warn('Polygon geofence must have at least 3 coordinates'); return false; } for (const coord of polygonGeofence.coordinates) { if (!coord || typeof coord.latitude !== 'number' || typeof coord.longitude !== 'number') { console.warn('All polygon coordinates must be valid'); return false; } } } return true; } /** * Get center point of a geofence * @param geofence Geofence * @returns Center coordinate */ function getGeofenceCenter(geofence) { if (geofence.type === 'circle') { return geofence.center; } else { // Calculate centroid of polygon const coords = geofence.coordinates; const lat = coords.reduce((sum, c) => sum + c.latitude, 0) / coords.length; const lng = coords.reduce((sum, c) => sum + c.longitude, 0) / coords.length; return { latitude: lat, longitude: lng }; } } /** * Check if two geofences overlap * @param geofence1 First geofence * @param geofence2 Second geofence * @returns true if geofences overlap */ function doGeofencesOverlap(geofence1, geofence2) { // Simple bounding box check first const center1 = getGeofenceCenter(geofence1); const center2 = getGeofenceCenter(geofence2); const distance = calculateDistance(center1, center2); // Get approximate radius for each geofence const radius1 = getApproximateRadius(geofence1); const radius2 = getApproximateRadius(geofence2); // If centers are too far apart, no overlap if (distance > radius1 + radius2) { return false; } // For more accurate check, test if any corner of one is inside the other if (geofence1.type === 'polygon') { for (const coord of geofence1.coordinates) { if (isPointInGeofence(coord, geofence2)) { return true; } } } if (geofence2.type === 'polygon') { for (const coord of geofence2.coordinates) { if (isPointInGeofence(coord, geofence1)) { return true; } } } return false; } /** * Get approximate radius of a geofence * @param geofence Geofence * @returns Approximate radius in meters */ function getApproximateRadius(geofence) { if (geofence.type === 'circle') { return geofence.radius; } else { // For polygon, find furthest point from center const center = getGeofenceCenter(geofence); let maxDistance = 0; for (const coord of geofence.coordinates) { const distance = calculateDistance(center, coord); maxDistance = Math.max(maxDistance, distance); } return maxDistance; } } //# sourceMappingURL=geofencing.js.map