@usefy/use-geolocation 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ calculateBearing: () => calculateBearing,
24
+ haversineDistance: () => haversineDistance,
25
+ useGeolocation: () => useGeolocation
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/useGeolocation.ts
30
+ var import_react = require("react");
31
+
32
+ // src/utils.ts
33
+ function haversineDistance(lat1, lon1, lat2, lon2) {
34
+ const R = 6371e3;
35
+ const toRadians = (degrees) => degrees * (Math.PI / 180);
36
+ const \u03C61 = toRadians(lat1);
37
+ const \u03C62 = toRadians(lat2);
38
+ const \u0394\u03C6 = toRadians(lat2 - lat1);
39
+ const \u0394\u03BB = toRadians(lon2 - lon1);
40
+ const a = Math.sin(\u0394\u03C6 / 2) * Math.sin(\u0394\u03C6 / 2) + Math.cos(\u03C61) * Math.cos(\u03C62) * Math.sin(\u0394\u03BB / 2) * Math.sin(\u0394\u03BB / 2);
41
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
42
+ return R * c;
43
+ }
44
+ function calculateBearing(lat1, lon1, lat2, lon2) {
45
+ const toRadians = (degrees) => degrees * (Math.PI / 180);
46
+ const toDegrees = (radians) => radians * (180 / Math.PI);
47
+ const \u03C61 = toRadians(lat1);
48
+ const \u03C62 = toRadians(lat2);
49
+ const \u0394\u03BB = toRadians(lon2 - lon1);
50
+ const y = Math.sin(\u0394\u03BB) * Math.cos(\u03C62);
51
+ const x = Math.cos(\u03C61) * Math.sin(\u03C62) - Math.sin(\u03C61) * Math.cos(\u03C62) * Math.cos(\u0394\u03BB);
52
+ const \u03B8 = Math.atan2(y, x);
53
+ const bearing = (toDegrees(\u03B8) + 360) % 360;
54
+ return bearing;
55
+ }
56
+
57
+ // src/useGeolocation.ts
58
+ function useGeolocation(options = {}) {
59
+ const {
60
+ enableHighAccuracy = false,
61
+ maximumAge = 0,
62
+ timeout = 3e4,
63
+ // Default 30 seconds
64
+ watch = false,
65
+ immediate = true,
66
+ onSuccess,
67
+ onError,
68
+ onPositionChange,
69
+ onPermissionChange
70
+ } = options;
71
+ const [position, setPosition] = (0, import_react.useState)(null);
72
+ const [loading, setLoading] = (0, import_react.useState)(false);
73
+ const [error, setError] = (0, import_react.useState)(null);
74
+ const [permission, setPermission] = (0, import_react.useState)("unavailable");
75
+ const isSupported = typeof navigator !== "undefined" && "geolocation" in navigator;
76
+ const onSuccessRef = (0, import_react.useRef)(onSuccess);
77
+ const onErrorRef = (0, import_react.useRef)(onError);
78
+ const onPositionChangeRef = (0, import_react.useRef)(onPositionChange);
79
+ const onPermissionChangeRef = (0, import_react.useRef)(onPermissionChange);
80
+ onSuccessRef.current = onSuccess;
81
+ onErrorRef.current = onError;
82
+ onPositionChangeRef.current = onPositionChange;
83
+ onPermissionChangeRef.current = onPermissionChange;
84
+ const watchIdRef = (0, import_react.useRef)(null);
85
+ const optionsRef = (0, import_react.useRef)({
86
+ enableHighAccuracy,
87
+ maximumAge,
88
+ timeout
89
+ });
90
+ const handleError = (0, import_react.useCallback)((nativeError) => {
91
+ setLoading(false);
92
+ let errorCode;
93
+ let errorMessage;
94
+ switch (nativeError.code) {
95
+ case nativeError.PERMISSION_DENIED:
96
+ errorCode = "PERMISSION_DENIED";
97
+ errorMessage = "User denied geolocation permission";
98
+ break;
99
+ case nativeError.POSITION_UNAVAILABLE:
100
+ errorCode = "POSITION_UNAVAILABLE";
101
+ errorMessage = "Position information unavailable";
102
+ break;
103
+ case nativeError.TIMEOUT:
104
+ errorCode = "TIMEOUT";
105
+ errorMessage = "Position request timed out";
106
+ break;
107
+ default:
108
+ errorCode = "POSITION_UNAVAILABLE";
109
+ errorMessage = "Unknown error occurred";
110
+ }
111
+ const geolocationError = {
112
+ code: errorCode,
113
+ message: errorMessage,
114
+ nativeError
115
+ };
116
+ setError(geolocationError);
117
+ onErrorRef.current?.(geolocationError);
118
+ }, []);
119
+ const handleSuccess = (0, import_react.useCallback)(
120
+ (nativePosition) => {
121
+ setLoading(false);
122
+ setError(null);
123
+ const geoPosition = {
124
+ coords: {
125
+ latitude: nativePosition.coords.latitude,
126
+ longitude: nativePosition.coords.longitude,
127
+ altitude: nativePosition.coords.altitude,
128
+ accuracy: nativePosition.coords.accuracy,
129
+ altitudeAccuracy: nativePosition.coords.altitudeAccuracy,
130
+ heading: nativePosition.coords.heading,
131
+ speed: nativePosition.coords.speed
132
+ },
133
+ timestamp: nativePosition.timestamp
134
+ };
135
+ setPosition(geoPosition);
136
+ onSuccessRef.current?.(geoPosition);
137
+ },
138
+ []
139
+ );
140
+ const getCurrentPosition = (0, import_react.useCallback)(() => {
141
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
142
+ const notSupportedError = {
143
+ code: "NOT_SUPPORTED",
144
+ message: "Geolocation is not supported in this environment"
145
+ };
146
+ setError(notSupportedError);
147
+ onErrorRef.current?.(notSupportedError);
148
+ return;
149
+ }
150
+ setLoading(true);
151
+ setError(null);
152
+ navigator.geolocation.getCurrentPosition(
153
+ handleSuccess,
154
+ handleError,
155
+ optionsRef.current
156
+ );
157
+ }, [handleSuccess, handleError]);
158
+ const clearWatch = (0, import_react.useCallback)(() => {
159
+ if (watchIdRef.current !== null && typeof navigator !== "undefined") {
160
+ navigator.geolocation.clearWatch(watchIdRef.current);
161
+ watchIdRef.current = null;
162
+ }
163
+ }, []);
164
+ const watchPosition = (0, import_react.useCallback)(() => {
165
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
166
+ const notSupportedError = {
167
+ code: "NOT_SUPPORTED",
168
+ message: "Geolocation is not supported in this environment"
169
+ };
170
+ setError(notSupportedError);
171
+ onErrorRef.current?.(notSupportedError);
172
+ return;
173
+ }
174
+ clearWatch();
175
+ setLoading(true);
176
+ setError(null);
177
+ const handleWatchSuccess = (nativePosition) => {
178
+ handleSuccess(nativePosition);
179
+ const geoPosition = {
180
+ coords: {
181
+ latitude: nativePosition.coords.latitude,
182
+ longitude: nativePosition.coords.longitude,
183
+ altitude: nativePosition.coords.altitude,
184
+ accuracy: nativePosition.coords.accuracy,
185
+ altitudeAccuracy: nativePosition.coords.altitudeAccuracy,
186
+ heading: nativePosition.coords.heading,
187
+ speed: nativePosition.coords.speed
188
+ },
189
+ timestamp: nativePosition.timestamp
190
+ };
191
+ onPositionChangeRef.current?.(geoPosition);
192
+ };
193
+ watchIdRef.current = navigator.geolocation.watchPosition(
194
+ handleWatchSuccess,
195
+ handleError,
196
+ optionsRef.current
197
+ );
198
+ }, [handleSuccess, handleError, clearWatch]);
199
+ (0, import_react.useEffect)(() => {
200
+ optionsRef.current = {
201
+ enableHighAccuracy,
202
+ maximumAge,
203
+ timeout
204
+ };
205
+ if (watchIdRef.current !== null) {
206
+ clearWatch();
207
+ watchPosition();
208
+ }
209
+ }, [enableHighAccuracy, maximumAge, timeout]);
210
+ (0, import_react.useEffect)(() => {
211
+ if (typeof navigator === "undefined" || !navigator.permissions) {
212
+ setPermission("unavailable");
213
+ return;
214
+ }
215
+ let permissionStatus = null;
216
+ let changeHandler = null;
217
+ navigator.permissions.query({ name: "geolocation" }).then((status) => {
218
+ permissionStatus = status;
219
+ setPermission(status.state);
220
+ changeHandler = () => {
221
+ const newState = status.state;
222
+ setPermission(newState);
223
+ onPermissionChangeRef.current?.(newState);
224
+ };
225
+ status.addEventListener("change", changeHandler);
226
+ }).catch(() => {
227
+ setPermission("unavailable");
228
+ });
229
+ return () => {
230
+ if (permissionStatus && changeHandler) {
231
+ permissionStatus.removeEventListener("change", changeHandler);
232
+ }
233
+ };
234
+ }, []);
235
+ (0, import_react.useEffect)(() => {
236
+ if (immediate && isSupported) {
237
+ getCurrentPosition();
238
+ }
239
+ }, [immediate, isSupported]);
240
+ (0, import_react.useEffect)(() => {
241
+ if (watch && isSupported) {
242
+ watchPosition();
243
+ }
244
+ return () => {
245
+ clearWatch();
246
+ };
247
+ }, [watch, isSupported]);
248
+ (0, import_react.useEffect)(() => {
249
+ return () => {
250
+ clearWatch();
251
+ };
252
+ }, []);
253
+ const distanceFrom = (0, import_react.useCallback)(
254
+ (latitude, longitude) => {
255
+ if (!position) {
256
+ return null;
257
+ }
258
+ return haversineDistance(
259
+ position.coords.latitude,
260
+ position.coords.longitude,
261
+ latitude,
262
+ longitude
263
+ );
264
+ },
265
+ [position]
266
+ );
267
+ const bearingTo = (0, import_react.useCallback)(
268
+ (latitude, longitude) => {
269
+ if (!position) {
270
+ return null;
271
+ }
272
+ return calculateBearing(
273
+ position.coords.latitude,
274
+ position.coords.longitude,
275
+ latitude,
276
+ longitude
277
+ );
278
+ },
279
+ [position]
280
+ );
281
+ return {
282
+ position,
283
+ loading,
284
+ error,
285
+ permission,
286
+ isSupported,
287
+ getCurrentPosition,
288
+ watchPosition,
289
+ clearWatch,
290
+ distanceFrom,
291
+ bearingTo
292
+ };
293
+ }
294
+ // Annotate the CommonJS export names for ESM import in node:
295
+ 0 && (module.exports = {
296
+ calculateBearing,
297
+ haversineDistance,
298
+ useGeolocation
299
+ });
300
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/useGeolocation.ts","../src/utils.ts"],"sourcesContent":["export { useGeolocation } from \"./useGeolocation\";\nexport type {\n GeoCoordinates,\n GeoPosition,\n GeolocationError,\n GeolocationErrorCode,\n PermissionState,\n UseGeolocationOptions,\n UseGeolocationReturn,\n} from \"./types\";\nexport { haversineDistance, calculateBearing } from \"./utils\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type {\n GeolocationError,\n GeolocationErrorCode,\n GeoPosition,\n PermissionState,\n UseGeolocationOptions,\n UseGeolocationReturn,\n} from \"./types\";\nimport { calculateBearing, haversineDistance } from \"./utils\";\n\n/**\n * A React hook for accessing device geolocation with real-time tracking and distance calculation.\n *\n * Features:\n * - Get current position (one-time)\n * - Watch position for real-time updates\n * - Permission state tracking\n * - Distance calculation using Haversine formula\n * - Bearing/direction calculation\n * - SSR compatible\n * - TypeScript support\n *\n * @param options - Configuration options\n * @returns Geolocation state and control functions\n *\n * @example\n * ```tsx\n * // Basic usage - get current position\n * function MyLocation() {\n * const { position, loading, error } = useGeolocation();\n *\n * if (loading) return <p>Loading location...</p>;\n * if (error) return <p>Error: {error.message}</p>;\n * if (!position) return <p>No position yet</p>;\n *\n * return (\n * <div>\n * <p>Latitude: {position.coords.latitude}</p>\n * <p>Longitude: {position.coords.longitude}</p>\n * <p>Accuracy: {position.coords.accuracy}m</p>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Real-time tracking with watch\n * function LiveTracking() {\n * const { position, watchPosition, clearWatch } = useGeolocation({\n * immediate: false,\n * watch: false,\n * });\n *\n * return (\n * <div>\n * <button onClick={watchPosition}>Start Tracking</button>\n * <button onClick={clearWatch}>Stop Tracking</button>\n * {position && (\n * <p>Current: {position.coords.latitude}, {position.coords.longitude}</p>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Distance calculation\n * function DistanceToDestination() {\n * const { position, distanceFrom } = useGeolocation();\n *\n * // New York City coordinates\n * const nyLat = 40.7128;\n * const nyLon = -74.0060;\n *\n * const distance = distanceFrom(nyLat, nyLon);\n *\n * return (\n * <div>\n * {distance && (\n * <p>Distance to NYC: {(distance / 1000).toFixed(2)} km</p>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With callbacks and high accuracy\n * const geolocation = useGeolocation({\n * enableHighAccuracy: true,\n * timeout: 10000,\n * onSuccess: (pos) => console.log('Got position:', pos),\n * onError: (err) => console.error('Geolocation error:', err),\n * onPositionChange: (pos) => console.log('Position updated:', pos),\n * onPermissionChange: (state) => console.log('Permission:', state),\n * });\n * ```\n */\nexport function useGeolocation(\n options: UseGeolocationOptions = {}\n): UseGeolocationReturn {\n // ============ Parse Options ============\n const {\n enableHighAccuracy = false,\n maximumAge = 0,\n timeout = 30000, // Default 30 seconds\n watch = false,\n immediate = true,\n onSuccess,\n onError,\n onPositionChange,\n onPermissionChange,\n } = options;\n\n // ============ State ============\n const [position, setPosition] = useState<GeoPosition | null>(null);\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<GeolocationError | null>(null);\n const [permission, setPermission] = useState<PermissionState>(\"unavailable\");\n\n // ============ Check Support ============\n const isSupported =\n typeof navigator !== \"undefined\" && \"geolocation\" in navigator;\n\n // ============ Refs for Callbacks ============\n // Store callbacks in refs to avoid re-registering listeners when they change\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n const onPositionChangeRef = useRef(onPositionChange);\n const onPermissionChangeRef = useRef(onPermissionChange);\n\n // Update callback refs on every render\n onSuccessRef.current = onSuccess;\n onErrorRef.current = onError;\n onPositionChangeRef.current = onPositionChange;\n onPermissionChangeRef.current = onPermissionChange;\n\n // ============ Refs for Watch Management ============\n const watchIdRef = useRef<number | null>(null);\n\n // ============ Refs for Options ============\n const optionsRef = useRef<PositionOptions>({\n enableHighAccuracy,\n maximumAge,\n timeout,\n });\n\n // ============ Error Handler ============\n const handleError = useCallback((nativeError: GeolocationPositionError) => {\n setLoading(false);\n\n let errorCode: GeolocationErrorCode;\n let errorMessage: string;\n\n switch (nativeError.code) {\n case nativeError.PERMISSION_DENIED:\n errorCode = \"PERMISSION_DENIED\";\n errorMessage = \"User denied geolocation permission\";\n break;\n case nativeError.POSITION_UNAVAILABLE:\n errorCode = \"POSITION_UNAVAILABLE\";\n errorMessage = \"Position information unavailable\";\n break;\n case nativeError.TIMEOUT:\n errorCode = \"TIMEOUT\";\n errorMessage = \"Position request timed out\";\n break;\n default:\n errorCode = \"POSITION_UNAVAILABLE\";\n errorMessage = \"Unknown error occurred\";\n }\n\n const geolocationError: GeolocationError = {\n code: errorCode,\n message: errorMessage,\n nativeError,\n };\n\n setError(geolocationError);\n onErrorRef.current?.(geolocationError);\n }, []);\n\n // ============ Success Handler ============\n const handleSuccess = useCallback(\n (nativePosition: globalThis.GeolocationPosition) => {\n setLoading(false);\n setError(null);\n\n // Convert to plain object to avoid issues with frozen/readonly native object\n const geoPosition: GeoPosition = {\n coords: {\n latitude: nativePosition.coords.latitude,\n longitude: nativePosition.coords.longitude,\n altitude: nativePosition.coords.altitude,\n accuracy: nativePosition.coords.accuracy,\n altitudeAccuracy: nativePosition.coords.altitudeAccuracy,\n heading: nativePosition.coords.heading,\n speed: nativePosition.coords.speed,\n },\n timestamp: nativePosition.timestamp,\n };\n\n setPosition(geoPosition);\n onSuccessRef.current?.(geoPosition);\n },\n []\n );\n\n // ============ getCurrentPosition ============\n const getCurrentPosition = useCallback(() => {\n // Check support dynamically in case it changes\n if (typeof navigator === \"undefined\" || !navigator.geolocation) {\n const notSupportedError: GeolocationError = {\n code: \"NOT_SUPPORTED\",\n message: \"Geolocation is not supported in this environment\",\n };\n setError(notSupportedError);\n onErrorRef.current?.(notSupportedError);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n navigator.geolocation.getCurrentPosition(\n handleSuccess,\n handleError,\n optionsRef.current\n );\n }, [handleSuccess, handleError]);\n\n // ============ clearWatch ============\n const clearWatch = useCallback(() => {\n if (watchIdRef.current !== null && typeof navigator !== \"undefined\") {\n navigator.geolocation.clearWatch(watchIdRef.current);\n watchIdRef.current = null;\n }\n }, []);\n\n // ============ watchPosition ============\n const watchPosition = useCallback(() => {\n // Check support dynamically in case it changes\n if (typeof navigator === \"undefined\" || !navigator.geolocation) {\n const notSupportedError: GeolocationError = {\n code: \"NOT_SUPPORTED\",\n message: \"Geolocation is not supported in this environment\",\n };\n setError(notSupportedError);\n onErrorRef.current?.(notSupportedError);\n return;\n }\n\n // Clear existing watch if any\n clearWatch();\n\n setLoading(true);\n setError(null);\n\n // Success handler for watch includes onPositionChange callback\n const handleWatchSuccess = (nativePosition: globalThis.GeolocationPosition) => {\n handleSuccess(nativePosition);\n\n // Convert to plain object for callback\n const geoPosition: GeoPosition = {\n coords: {\n latitude: nativePosition.coords.latitude,\n longitude: nativePosition.coords.longitude,\n altitude: nativePosition.coords.altitude,\n accuracy: nativePosition.coords.accuracy,\n altitudeAccuracy: nativePosition.coords.altitudeAccuracy,\n heading: nativePosition.coords.heading,\n speed: nativePosition.coords.speed,\n },\n timestamp: nativePosition.timestamp,\n };\n\n onPositionChangeRef.current?.(geoPosition);\n };\n\n watchIdRef.current = navigator.geolocation.watchPosition(\n handleWatchSuccess,\n handleError,\n optionsRef.current\n );\n }, [handleSuccess, handleError, clearWatch]);\n\n // ============ Update Options Ref & Auto-Restart Watch ============\n useEffect(() => {\n optionsRef.current = {\n enableHighAccuracy,\n maximumAge,\n timeout,\n };\n\n // If currently watching, restart with new options\n if (watchIdRef.current !== null) {\n clearWatch();\n watchPosition();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enableHighAccuracy, maximumAge, timeout]);\n // Note: clearWatch and watchPosition are intentionally omitted to avoid infinite loop\n\n // ============ Permission Monitoring ============\n useEffect(() => {\n if (typeof navigator === \"undefined\" || !navigator.permissions) {\n setPermission(\"unavailable\");\n return;\n }\n\n let permissionStatus: PermissionStatus | null = null;\n let changeHandler: (() => void) | null = null;\n\n navigator.permissions\n .query({ name: \"geolocation\" as PermissionName })\n .then((status) => {\n permissionStatus = status;\n setPermission(status.state as PermissionState);\n\n changeHandler = () => {\n const newState = status.state as PermissionState;\n setPermission(newState);\n onPermissionChangeRef.current?.(newState);\n };\n\n status.addEventListener(\"change\", changeHandler);\n })\n .catch(() => {\n // Permissions API not supported or query failed\n setPermission(\"unavailable\");\n });\n\n return () => {\n if (permissionStatus && changeHandler) {\n permissionStatus.removeEventListener(\"change\", changeHandler);\n }\n };\n }, []);\n\n // ============ Immediate Fetch on Mount ============\n useEffect(() => {\n if (immediate && isSupported) {\n getCurrentPosition();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [immediate, isSupported]);\n // getCurrentPosition is intentionally omitted to run only once on mount\n\n // ============ Watch on Mount ============\n useEffect(() => {\n if (watch && isSupported) {\n watchPosition();\n }\n\n return () => {\n clearWatch();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [watch, isSupported]);\n // watchPosition and clearWatch are intentionally omitted to run only once on mount\n\n // ============ Cleanup on Unmount ============\n useEffect(() => {\n return () => {\n clearWatch();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ============ Utility: distanceFrom ============\n const distanceFrom = useCallback(\n (latitude: number, longitude: number): number | null => {\n if (!position) {\n return null;\n }\n\n return haversineDistance(\n position.coords.latitude,\n position.coords.longitude,\n latitude,\n longitude\n );\n },\n [position]\n );\n\n // ============ Utility: bearingTo ============\n const bearingTo = useCallback(\n (latitude: number, longitude: number): number | null => {\n if (!position) {\n return null;\n }\n\n return calculateBearing(\n position.coords.latitude,\n position.coords.longitude,\n latitude,\n longitude\n );\n },\n [position]\n );\n\n // ============ Return ============\n return {\n position,\n loading,\n error,\n permission,\n isSupported,\n getCurrentPosition,\n watchPosition,\n clearWatch,\n distanceFrom,\n bearingTo,\n };\n}\n","/**\n * Calculate distance between two points using Haversine formula\n *\n * The Haversine formula determines the great-circle distance between two points\n * on a sphere given their longitudes and latitudes. This assumes a spherical Earth,\n * which introduces an error of up to 0.5% compared to more accurate ellipsoidal models.\n *\n * @param lat1 - Latitude of first point in decimal degrees\n * @param lon1 - Longitude of first point in decimal degrees\n * @param lat2 - Latitude of second point in decimal degrees\n * @param lon2 - Longitude of second point in decimal degrees\n * @returns Distance in meters\n *\n * @example\n * ```typescript\n * // Distance from San Francisco to Los Angeles\n * const distance = haversineDistance(37.7749, -122.4194, 34.0522, -118.2437);\n * console.log(distance); // ~559,000 meters (559 km)\n * ```\n */\nexport function haversineDistance(\n lat1: number,\n lon1: number,\n lat2: number,\n lon2: number\n): number {\n // Earth's radius in meters\n const R = 6371000;\n\n // Convert degrees to radians\n const toRadians = (degrees: number): number => degrees * (Math.PI / 180);\n\n // Convert all coordinates to radians\n const φ1 = toRadians(lat1);\n const φ2 = toRadians(lat2);\n const Δφ = toRadians(lat2 - lat1);\n const Δλ = toRadians(lon2 - lon1);\n\n // Haversine formula\n const a =\n Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +\n Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);\n\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n // Distance in meters\n return R * c;\n}\n\n/**\n * Calculate initial bearing (forward azimuth) from point 1 to point 2\n *\n * The bearing is the direction you would need to travel from the first point\n * to reach the second point along a great circle path. Note that the bearing\n * may change along the path (except when traveling due north/south or along the equator).\n *\n * @param lat1 - Latitude of first point in decimal degrees\n * @param lon1 - Longitude of first point in decimal degrees\n * @param lat2 - Latitude of second point in decimal degrees\n * @param lon2 - Longitude of second point in decimal degrees\n * @returns Bearing in degrees (0-360, where 0=North, 90=East, 180=South, 270=West)\n *\n * @example\n * ```typescript\n * // Bearing from New York to London\n * const bearing = calculateBearing(40.7128, -74.0060, 51.5074, -0.1278);\n * console.log(bearing); // ~51 degrees (Northeast)\n * ```\n */\nexport function calculateBearing(\n lat1: number,\n lon1: number,\n lat2: number,\n lon2: number\n): number {\n // Convert degrees to radians\n const toRadians = (degrees: number): number => degrees * (Math.PI / 180);\n const toDegrees = (radians: number): number => radians * (180 / Math.PI);\n\n // Convert all coordinates to radians\n const φ1 = toRadians(lat1);\n const φ2 = toRadians(lat2);\n const Δλ = toRadians(lon2 - lon1);\n\n // Calculate bearing using forward azimuth formula\n const y = Math.sin(Δλ) * Math.cos(φ2);\n const x =\n Math.cos(φ1) * Math.sin(φ2) -\n Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ);\n\n const θ = Math.atan2(y, x);\n\n // Convert to degrees and normalize to 0-360 range\n const bearing = (toDegrees(θ) + 360) % 360;\n\n return bearing;\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,271 @@
1
+ // src/useGeolocation.ts
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+
4
+ // src/utils.ts
5
+ function haversineDistance(lat1, lon1, lat2, lon2) {
6
+ const R = 6371e3;
7
+ const toRadians = (degrees) => degrees * (Math.PI / 180);
8
+ const \u03C61 = toRadians(lat1);
9
+ const \u03C62 = toRadians(lat2);
10
+ const \u0394\u03C6 = toRadians(lat2 - lat1);
11
+ const \u0394\u03BB = toRadians(lon2 - lon1);
12
+ const a = Math.sin(\u0394\u03C6 / 2) * Math.sin(\u0394\u03C6 / 2) + Math.cos(\u03C61) * Math.cos(\u03C62) * Math.sin(\u0394\u03BB / 2) * Math.sin(\u0394\u03BB / 2);
13
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
14
+ return R * c;
15
+ }
16
+ function calculateBearing(lat1, lon1, lat2, lon2) {
17
+ const toRadians = (degrees) => degrees * (Math.PI / 180);
18
+ const toDegrees = (radians) => radians * (180 / Math.PI);
19
+ const \u03C61 = toRadians(lat1);
20
+ const \u03C62 = toRadians(lat2);
21
+ const \u0394\u03BB = toRadians(lon2 - lon1);
22
+ const y = Math.sin(\u0394\u03BB) * Math.cos(\u03C62);
23
+ const x = Math.cos(\u03C61) * Math.sin(\u03C62) - Math.sin(\u03C61) * Math.cos(\u03C62) * Math.cos(\u0394\u03BB);
24
+ const \u03B8 = Math.atan2(y, x);
25
+ const bearing = (toDegrees(\u03B8) + 360) % 360;
26
+ return bearing;
27
+ }
28
+
29
+ // src/useGeolocation.ts
30
+ function useGeolocation(options = {}) {
31
+ const {
32
+ enableHighAccuracy = false,
33
+ maximumAge = 0,
34
+ timeout = 3e4,
35
+ // Default 30 seconds
36
+ watch = false,
37
+ immediate = true,
38
+ onSuccess,
39
+ onError,
40
+ onPositionChange,
41
+ onPermissionChange
42
+ } = options;
43
+ const [position, setPosition] = useState(null);
44
+ const [loading, setLoading] = useState(false);
45
+ const [error, setError] = useState(null);
46
+ const [permission, setPermission] = useState("unavailable");
47
+ const isSupported = typeof navigator !== "undefined" && "geolocation" in navigator;
48
+ const onSuccessRef = useRef(onSuccess);
49
+ const onErrorRef = useRef(onError);
50
+ const onPositionChangeRef = useRef(onPositionChange);
51
+ const onPermissionChangeRef = useRef(onPermissionChange);
52
+ onSuccessRef.current = onSuccess;
53
+ onErrorRef.current = onError;
54
+ onPositionChangeRef.current = onPositionChange;
55
+ onPermissionChangeRef.current = onPermissionChange;
56
+ const watchIdRef = useRef(null);
57
+ const optionsRef = useRef({
58
+ enableHighAccuracy,
59
+ maximumAge,
60
+ timeout
61
+ });
62
+ const handleError = useCallback((nativeError) => {
63
+ setLoading(false);
64
+ let errorCode;
65
+ let errorMessage;
66
+ switch (nativeError.code) {
67
+ case nativeError.PERMISSION_DENIED:
68
+ errorCode = "PERMISSION_DENIED";
69
+ errorMessage = "User denied geolocation permission";
70
+ break;
71
+ case nativeError.POSITION_UNAVAILABLE:
72
+ errorCode = "POSITION_UNAVAILABLE";
73
+ errorMessage = "Position information unavailable";
74
+ break;
75
+ case nativeError.TIMEOUT:
76
+ errorCode = "TIMEOUT";
77
+ errorMessage = "Position request timed out";
78
+ break;
79
+ default:
80
+ errorCode = "POSITION_UNAVAILABLE";
81
+ errorMessage = "Unknown error occurred";
82
+ }
83
+ const geolocationError = {
84
+ code: errorCode,
85
+ message: errorMessage,
86
+ nativeError
87
+ };
88
+ setError(geolocationError);
89
+ onErrorRef.current?.(geolocationError);
90
+ }, []);
91
+ const handleSuccess = useCallback(
92
+ (nativePosition) => {
93
+ setLoading(false);
94
+ setError(null);
95
+ const geoPosition = {
96
+ coords: {
97
+ latitude: nativePosition.coords.latitude,
98
+ longitude: nativePosition.coords.longitude,
99
+ altitude: nativePosition.coords.altitude,
100
+ accuracy: nativePosition.coords.accuracy,
101
+ altitudeAccuracy: nativePosition.coords.altitudeAccuracy,
102
+ heading: nativePosition.coords.heading,
103
+ speed: nativePosition.coords.speed
104
+ },
105
+ timestamp: nativePosition.timestamp
106
+ };
107
+ setPosition(geoPosition);
108
+ onSuccessRef.current?.(geoPosition);
109
+ },
110
+ []
111
+ );
112
+ const getCurrentPosition = useCallback(() => {
113
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
114
+ const notSupportedError = {
115
+ code: "NOT_SUPPORTED",
116
+ message: "Geolocation is not supported in this environment"
117
+ };
118
+ setError(notSupportedError);
119
+ onErrorRef.current?.(notSupportedError);
120
+ return;
121
+ }
122
+ setLoading(true);
123
+ setError(null);
124
+ navigator.geolocation.getCurrentPosition(
125
+ handleSuccess,
126
+ handleError,
127
+ optionsRef.current
128
+ );
129
+ }, [handleSuccess, handleError]);
130
+ const clearWatch = useCallback(() => {
131
+ if (watchIdRef.current !== null && typeof navigator !== "undefined") {
132
+ navigator.geolocation.clearWatch(watchIdRef.current);
133
+ watchIdRef.current = null;
134
+ }
135
+ }, []);
136
+ const watchPosition = useCallback(() => {
137
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
138
+ const notSupportedError = {
139
+ code: "NOT_SUPPORTED",
140
+ message: "Geolocation is not supported in this environment"
141
+ };
142
+ setError(notSupportedError);
143
+ onErrorRef.current?.(notSupportedError);
144
+ return;
145
+ }
146
+ clearWatch();
147
+ setLoading(true);
148
+ setError(null);
149
+ const handleWatchSuccess = (nativePosition) => {
150
+ handleSuccess(nativePosition);
151
+ const geoPosition = {
152
+ coords: {
153
+ latitude: nativePosition.coords.latitude,
154
+ longitude: nativePosition.coords.longitude,
155
+ altitude: nativePosition.coords.altitude,
156
+ accuracy: nativePosition.coords.accuracy,
157
+ altitudeAccuracy: nativePosition.coords.altitudeAccuracy,
158
+ heading: nativePosition.coords.heading,
159
+ speed: nativePosition.coords.speed
160
+ },
161
+ timestamp: nativePosition.timestamp
162
+ };
163
+ onPositionChangeRef.current?.(geoPosition);
164
+ };
165
+ watchIdRef.current = navigator.geolocation.watchPosition(
166
+ handleWatchSuccess,
167
+ handleError,
168
+ optionsRef.current
169
+ );
170
+ }, [handleSuccess, handleError, clearWatch]);
171
+ useEffect(() => {
172
+ optionsRef.current = {
173
+ enableHighAccuracy,
174
+ maximumAge,
175
+ timeout
176
+ };
177
+ if (watchIdRef.current !== null) {
178
+ clearWatch();
179
+ watchPosition();
180
+ }
181
+ }, [enableHighAccuracy, maximumAge, timeout]);
182
+ useEffect(() => {
183
+ if (typeof navigator === "undefined" || !navigator.permissions) {
184
+ setPermission("unavailable");
185
+ return;
186
+ }
187
+ let permissionStatus = null;
188
+ let changeHandler = null;
189
+ navigator.permissions.query({ name: "geolocation" }).then((status) => {
190
+ permissionStatus = status;
191
+ setPermission(status.state);
192
+ changeHandler = () => {
193
+ const newState = status.state;
194
+ setPermission(newState);
195
+ onPermissionChangeRef.current?.(newState);
196
+ };
197
+ status.addEventListener("change", changeHandler);
198
+ }).catch(() => {
199
+ setPermission("unavailable");
200
+ });
201
+ return () => {
202
+ if (permissionStatus && changeHandler) {
203
+ permissionStatus.removeEventListener("change", changeHandler);
204
+ }
205
+ };
206
+ }, []);
207
+ useEffect(() => {
208
+ if (immediate && isSupported) {
209
+ getCurrentPosition();
210
+ }
211
+ }, [immediate, isSupported]);
212
+ useEffect(() => {
213
+ if (watch && isSupported) {
214
+ watchPosition();
215
+ }
216
+ return () => {
217
+ clearWatch();
218
+ };
219
+ }, [watch, isSupported]);
220
+ useEffect(() => {
221
+ return () => {
222
+ clearWatch();
223
+ };
224
+ }, []);
225
+ const distanceFrom = useCallback(
226
+ (latitude, longitude) => {
227
+ if (!position) {
228
+ return null;
229
+ }
230
+ return haversineDistance(
231
+ position.coords.latitude,
232
+ position.coords.longitude,
233
+ latitude,
234
+ longitude
235
+ );
236
+ },
237
+ [position]
238
+ );
239
+ const bearingTo = useCallback(
240
+ (latitude, longitude) => {
241
+ if (!position) {
242
+ return null;
243
+ }
244
+ return calculateBearing(
245
+ position.coords.latitude,
246
+ position.coords.longitude,
247
+ latitude,
248
+ longitude
249
+ );
250
+ },
251
+ [position]
252
+ );
253
+ return {
254
+ position,
255
+ loading,
256
+ error,
257
+ permission,
258
+ isSupported,
259
+ getCurrentPosition,
260
+ watchPosition,
261
+ clearWatch,
262
+ distanceFrom,
263
+ bearingTo
264
+ };
265
+ }
266
+ export {
267
+ calculateBearing,
268
+ haversineDistance,
269
+ useGeolocation
270
+ };
271
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useGeolocation.ts","../src/utils.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type {\n GeolocationError,\n GeolocationErrorCode,\n GeoPosition,\n PermissionState,\n UseGeolocationOptions,\n UseGeolocationReturn,\n} from \"./types\";\nimport { calculateBearing, haversineDistance } from \"./utils\";\n\n/**\n * A React hook for accessing device geolocation with real-time tracking and distance calculation.\n *\n * Features:\n * - Get current position (one-time)\n * - Watch position for real-time updates\n * - Permission state tracking\n * - Distance calculation using Haversine formula\n * - Bearing/direction calculation\n * - SSR compatible\n * - TypeScript support\n *\n * @param options - Configuration options\n * @returns Geolocation state and control functions\n *\n * @example\n * ```tsx\n * // Basic usage - get current position\n * function MyLocation() {\n * const { position, loading, error } = useGeolocation();\n *\n * if (loading) return <p>Loading location...</p>;\n * if (error) return <p>Error: {error.message}</p>;\n * if (!position) return <p>No position yet</p>;\n *\n * return (\n * <div>\n * <p>Latitude: {position.coords.latitude}</p>\n * <p>Longitude: {position.coords.longitude}</p>\n * <p>Accuracy: {position.coords.accuracy}m</p>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Real-time tracking with watch\n * function LiveTracking() {\n * const { position, watchPosition, clearWatch } = useGeolocation({\n * immediate: false,\n * watch: false,\n * });\n *\n * return (\n * <div>\n * <button onClick={watchPosition}>Start Tracking</button>\n * <button onClick={clearWatch}>Stop Tracking</button>\n * {position && (\n * <p>Current: {position.coords.latitude}, {position.coords.longitude}</p>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Distance calculation\n * function DistanceToDestination() {\n * const { position, distanceFrom } = useGeolocation();\n *\n * // New York City coordinates\n * const nyLat = 40.7128;\n * const nyLon = -74.0060;\n *\n * const distance = distanceFrom(nyLat, nyLon);\n *\n * return (\n * <div>\n * {distance && (\n * <p>Distance to NYC: {(distance / 1000).toFixed(2)} km</p>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With callbacks and high accuracy\n * const geolocation = useGeolocation({\n * enableHighAccuracy: true,\n * timeout: 10000,\n * onSuccess: (pos) => console.log('Got position:', pos),\n * onError: (err) => console.error('Geolocation error:', err),\n * onPositionChange: (pos) => console.log('Position updated:', pos),\n * onPermissionChange: (state) => console.log('Permission:', state),\n * });\n * ```\n */\nexport function useGeolocation(\n options: UseGeolocationOptions = {}\n): UseGeolocationReturn {\n // ============ Parse Options ============\n const {\n enableHighAccuracy = false,\n maximumAge = 0,\n timeout = 30000, // Default 30 seconds\n watch = false,\n immediate = true,\n onSuccess,\n onError,\n onPositionChange,\n onPermissionChange,\n } = options;\n\n // ============ State ============\n const [position, setPosition] = useState<GeoPosition | null>(null);\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<GeolocationError | null>(null);\n const [permission, setPermission] = useState<PermissionState>(\"unavailable\");\n\n // ============ Check Support ============\n const isSupported =\n typeof navigator !== \"undefined\" && \"geolocation\" in navigator;\n\n // ============ Refs for Callbacks ============\n // Store callbacks in refs to avoid re-registering listeners when they change\n const onSuccessRef = useRef(onSuccess);\n const onErrorRef = useRef(onError);\n const onPositionChangeRef = useRef(onPositionChange);\n const onPermissionChangeRef = useRef(onPermissionChange);\n\n // Update callback refs on every render\n onSuccessRef.current = onSuccess;\n onErrorRef.current = onError;\n onPositionChangeRef.current = onPositionChange;\n onPermissionChangeRef.current = onPermissionChange;\n\n // ============ Refs for Watch Management ============\n const watchIdRef = useRef<number | null>(null);\n\n // ============ Refs for Options ============\n const optionsRef = useRef<PositionOptions>({\n enableHighAccuracy,\n maximumAge,\n timeout,\n });\n\n // ============ Error Handler ============\n const handleError = useCallback((nativeError: GeolocationPositionError) => {\n setLoading(false);\n\n let errorCode: GeolocationErrorCode;\n let errorMessage: string;\n\n switch (nativeError.code) {\n case nativeError.PERMISSION_DENIED:\n errorCode = \"PERMISSION_DENIED\";\n errorMessage = \"User denied geolocation permission\";\n break;\n case nativeError.POSITION_UNAVAILABLE:\n errorCode = \"POSITION_UNAVAILABLE\";\n errorMessage = \"Position information unavailable\";\n break;\n case nativeError.TIMEOUT:\n errorCode = \"TIMEOUT\";\n errorMessage = \"Position request timed out\";\n break;\n default:\n errorCode = \"POSITION_UNAVAILABLE\";\n errorMessage = \"Unknown error occurred\";\n }\n\n const geolocationError: GeolocationError = {\n code: errorCode,\n message: errorMessage,\n nativeError,\n };\n\n setError(geolocationError);\n onErrorRef.current?.(geolocationError);\n }, []);\n\n // ============ Success Handler ============\n const handleSuccess = useCallback(\n (nativePosition: globalThis.GeolocationPosition) => {\n setLoading(false);\n setError(null);\n\n // Convert to plain object to avoid issues with frozen/readonly native object\n const geoPosition: GeoPosition = {\n coords: {\n latitude: nativePosition.coords.latitude,\n longitude: nativePosition.coords.longitude,\n altitude: nativePosition.coords.altitude,\n accuracy: nativePosition.coords.accuracy,\n altitudeAccuracy: nativePosition.coords.altitudeAccuracy,\n heading: nativePosition.coords.heading,\n speed: nativePosition.coords.speed,\n },\n timestamp: nativePosition.timestamp,\n };\n\n setPosition(geoPosition);\n onSuccessRef.current?.(geoPosition);\n },\n []\n );\n\n // ============ getCurrentPosition ============\n const getCurrentPosition = useCallback(() => {\n // Check support dynamically in case it changes\n if (typeof navigator === \"undefined\" || !navigator.geolocation) {\n const notSupportedError: GeolocationError = {\n code: \"NOT_SUPPORTED\",\n message: \"Geolocation is not supported in this environment\",\n };\n setError(notSupportedError);\n onErrorRef.current?.(notSupportedError);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n navigator.geolocation.getCurrentPosition(\n handleSuccess,\n handleError,\n optionsRef.current\n );\n }, [handleSuccess, handleError]);\n\n // ============ clearWatch ============\n const clearWatch = useCallback(() => {\n if (watchIdRef.current !== null && typeof navigator !== \"undefined\") {\n navigator.geolocation.clearWatch(watchIdRef.current);\n watchIdRef.current = null;\n }\n }, []);\n\n // ============ watchPosition ============\n const watchPosition = useCallback(() => {\n // Check support dynamically in case it changes\n if (typeof navigator === \"undefined\" || !navigator.geolocation) {\n const notSupportedError: GeolocationError = {\n code: \"NOT_SUPPORTED\",\n message: \"Geolocation is not supported in this environment\",\n };\n setError(notSupportedError);\n onErrorRef.current?.(notSupportedError);\n return;\n }\n\n // Clear existing watch if any\n clearWatch();\n\n setLoading(true);\n setError(null);\n\n // Success handler for watch includes onPositionChange callback\n const handleWatchSuccess = (nativePosition: globalThis.GeolocationPosition) => {\n handleSuccess(nativePosition);\n\n // Convert to plain object for callback\n const geoPosition: GeoPosition = {\n coords: {\n latitude: nativePosition.coords.latitude,\n longitude: nativePosition.coords.longitude,\n altitude: nativePosition.coords.altitude,\n accuracy: nativePosition.coords.accuracy,\n altitudeAccuracy: nativePosition.coords.altitudeAccuracy,\n heading: nativePosition.coords.heading,\n speed: nativePosition.coords.speed,\n },\n timestamp: nativePosition.timestamp,\n };\n\n onPositionChangeRef.current?.(geoPosition);\n };\n\n watchIdRef.current = navigator.geolocation.watchPosition(\n handleWatchSuccess,\n handleError,\n optionsRef.current\n );\n }, [handleSuccess, handleError, clearWatch]);\n\n // ============ Update Options Ref & Auto-Restart Watch ============\n useEffect(() => {\n optionsRef.current = {\n enableHighAccuracy,\n maximumAge,\n timeout,\n };\n\n // If currently watching, restart with new options\n if (watchIdRef.current !== null) {\n clearWatch();\n watchPosition();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enableHighAccuracy, maximumAge, timeout]);\n // Note: clearWatch and watchPosition are intentionally omitted to avoid infinite loop\n\n // ============ Permission Monitoring ============\n useEffect(() => {\n if (typeof navigator === \"undefined\" || !navigator.permissions) {\n setPermission(\"unavailable\");\n return;\n }\n\n let permissionStatus: PermissionStatus | null = null;\n let changeHandler: (() => void) | null = null;\n\n navigator.permissions\n .query({ name: \"geolocation\" as PermissionName })\n .then((status) => {\n permissionStatus = status;\n setPermission(status.state as PermissionState);\n\n changeHandler = () => {\n const newState = status.state as PermissionState;\n setPermission(newState);\n onPermissionChangeRef.current?.(newState);\n };\n\n status.addEventListener(\"change\", changeHandler);\n })\n .catch(() => {\n // Permissions API not supported or query failed\n setPermission(\"unavailable\");\n });\n\n return () => {\n if (permissionStatus && changeHandler) {\n permissionStatus.removeEventListener(\"change\", changeHandler);\n }\n };\n }, []);\n\n // ============ Immediate Fetch on Mount ============\n useEffect(() => {\n if (immediate && isSupported) {\n getCurrentPosition();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [immediate, isSupported]);\n // getCurrentPosition is intentionally omitted to run only once on mount\n\n // ============ Watch on Mount ============\n useEffect(() => {\n if (watch && isSupported) {\n watchPosition();\n }\n\n return () => {\n clearWatch();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [watch, isSupported]);\n // watchPosition and clearWatch are intentionally omitted to run only once on mount\n\n // ============ Cleanup on Unmount ============\n useEffect(() => {\n return () => {\n clearWatch();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ============ Utility: distanceFrom ============\n const distanceFrom = useCallback(\n (latitude: number, longitude: number): number | null => {\n if (!position) {\n return null;\n }\n\n return haversineDistance(\n position.coords.latitude,\n position.coords.longitude,\n latitude,\n longitude\n );\n },\n [position]\n );\n\n // ============ Utility: bearingTo ============\n const bearingTo = useCallback(\n (latitude: number, longitude: number): number | null => {\n if (!position) {\n return null;\n }\n\n return calculateBearing(\n position.coords.latitude,\n position.coords.longitude,\n latitude,\n longitude\n );\n },\n [position]\n );\n\n // ============ Return ============\n return {\n position,\n loading,\n error,\n permission,\n isSupported,\n getCurrentPosition,\n watchPosition,\n clearWatch,\n distanceFrom,\n bearingTo,\n };\n}\n","/**\n * Calculate distance between two points using Haversine formula\n *\n * The Haversine formula determines the great-circle distance between two points\n * on a sphere given their longitudes and latitudes. This assumes a spherical Earth,\n * which introduces an error of up to 0.5% compared to more accurate ellipsoidal models.\n *\n * @param lat1 - Latitude of first point in decimal degrees\n * @param lon1 - Longitude of first point in decimal degrees\n * @param lat2 - Latitude of second point in decimal degrees\n * @param lon2 - Longitude of second point in decimal degrees\n * @returns Distance in meters\n *\n * @example\n * ```typescript\n * // Distance from San Francisco to Los Angeles\n * const distance = haversineDistance(37.7749, -122.4194, 34.0522, -118.2437);\n * console.log(distance); // ~559,000 meters (559 km)\n * ```\n */\nexport function haversineDistance(\n lat1: number,\n lon1: number,\n lat2: number,\n lon2: number\n): number {\n // Earth's radius in meters\n const R = 6371000;\n\n // Convert degrees to radians\n const toRadians = (degrees: number): number => degrees * (Math.PI / 180);\n\n // Convert all coordinates to radians\n const φ1 = toRadians(lat1);\n const φ2 = toRadians(lat2);\n const Δφ = toRadians(lat2 - lat1);\n const Δλ = toRadians(lon2 - lon1);\n\n // Haversine formula\n const a =\n Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +\n Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);\n\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n // Distance in meters\n return R * c;\n}\n\n/**\n * Calculate initial bearing (forward azimuth) from point 1 to point 2\n *\n * The bearing is the direction you would need to travel from the first point\n * to reach the second point along a great circle path. Note that the bearing\n * may change along the path (except when traveling due north/south or along the equator).\n *\n * @param lat1 - Latitude of first point in decimal degrees\n * @param lon1 - Longitude of first point in decimal degrees\n * @param lat2 - Latitude of second point in decimal degrees\n * @param lon2 - Longitude of second point in decimal degrees\n * @returns Bearing in degrees (0-360, where 0=North, 90=East, 180=South, 270=West)\n *\n * @example\n * ```typescript\n * // Bearing from New York to London\n * const bearing = calculateBearing(40.7128, -74.0060, 51.5074, -0.1278);\n * console.log(bearing); // ~51 degrees (Northeast)\n * ```\n */\nexport function calculateBearing(\n lat1: number,\n lon1: number,\n lat2: number,\n lon2: number\n): number {\n // Convert degrees to radians\n const toRadians = (degrees: number): number => degrees * (Math.PI / 180);\n const toDegrees = (radians: number): number => radians * (180 / Math.PI);\n\n // Convert all coordinates to radians\n const φ1 = toRadians(lat1);\n const φ2 = toRadians(lat2);\n const Δλ = toRadians(lon2 - lon1);\n\n // Calculate bearing using forward azimuth formula\n const y = Math.sin(Δλ) * Math.cos(φ2);\n const x =\n Math.cos(φ1) * Math.sin(φ2) -\n Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ);\n\n const θ = Math.atan2(y, x);\n\n // Convert to degrees and normalize to 0-360 range\n const bearing = (toDegrees(θ) + 360) % 360;\n\n return bearing;\n}\n"],"mappings":"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","names":[]}