@capgo/background-geolocation 7.0.10 → 7.0.11
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/README.md +19 -18
- package/android/src/main/java/com/capgo/capacitor_background_geolocation/BackgroundGeolocation.java +47 -10
- package/android/src/main/java/com/capgo/capacitor_background_geolocation/BackgroundGeolocationService.java +122 -22
- package/dist/docs.json +49 -13
- package/dist/esm/definitions.d.ts +28 -12
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +11 -2
- package/dist/esm/web.js +95 -6
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +95 -6
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +95 -6
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/Plugin.m +1 -1
- package/ios/Plugin/Plugin.swift +148 -17
- package/package.json +1 -1
package/ios/Plugin/Plugin.swift
CHANGED
|
@@ -42,6 +42,12 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
|
|
|
42
42
|
private var isUpdatingLocation: Bool = false
|
|
43
43
|
private var activeCallbackId: String?
|
|
44
44
|
private var audioPlayer: AVAudioPlayer?
|
|
45
|
+
private var plannedRoute: [[Double]] = []
|
|
46
|
+
private var isOffRoute: Bool = true
|
|
47
|
+
private var distanceThreshold: Double = 50.0 // Default distance threshold in meters
|
|
48
|
+
|
|
49
|
+
// Earth radius in meters for distance calculations
|
|
50
|
+
private static let EARTH_RADIUS_M: Double = 6371000.0
|
|
45
51
|
|
|
46
52
|
@objc override public func load() {
|
|
47
53
|
UIDevice.current.isBatteryMonitoringEnabled = true
|
|
@@ -149,6 +155,54 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
|
|
|
149
155
|
}
|
|
150
156
|
}
|
|
151
157
|
|
|
158
|
+
@objc func setPlannedRoute(_ call: CAPPluginCall) {
|
|
159
|
+
DispatchQueue.global(qos: .background).async { [weak self] in
|
|
160
|
+
guard let self = self else { return }
|
|
161
|
+
|
|
162
|
+
// Get the sound file path
|
|
163
|
+
guard let soundFile = call.getString("soundFile") else {
|
|
164
|
+
call.reject("Sound file is required")
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Get the route array
|
|
169
|
+
let routeArray = call.getArray("route", JSObject.self) ?? []
|
|
170
|
+
var route: [[Double]] = []
|
|
171
|
+
|
|
172
|
+
for routePoint in routeArray {
|
|
173
|
+
if let pointArray = routePoint as? [Double], pointArray.count == 2 {
|
|
174
|
+
route.append(pointArray)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Get distance threshold
|
|
179
|
+
let distance = call.getDouble("distance") ?? 50.0
|
|
180
|
+
|
|
181
|
+
// Set up audio player
|
|
182
|
+
let assetPath = "public/" + soundFile
|
|
183
|
+
let assetPathSplit = assetPath.components(separatedBy: ".")
|
|
184
|
+
guard let url = Bundle.main.url(forResource: assetPathSplit[0], withExtension: assetPathSplit[1]) else {
|
|
185
|
+
call.reject("Sound file not found: \(assetPath)")
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
do {
|
|
190
|
+
self.audioPlayer?.stop()
|
|
191
|
+
self.audioPlayer = nil
|
|
192
|
+
self.audioPlayer = try AVAudioPlayer(contentsOf: url)
|
|
193
|
+
|
|
194
|
+
// Store route configuration
|
|
195
|
+
self.plannedRoute = route
|
|
196
|
+
self.distanceThreshold = distance
|
|
197
|
+
self.isOffRoute = true
|
|
198
|
+
|
|
199
|
+
call.resolve()
|
|
200
|
+
} catch {
|
|
201
|
+
call.reject("Could not load the sound file: \(error.localizedDescription)")
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
152
206
|
private func startUpdatingLocation() {
|
|
153
207
|
// Avoid unnecessary calls to startUpdatingLocation, which can
|
|
154
208
|
// result in extraneous invocations of didFailWithError.
|
|
@@ -173,28 +227,104 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
|
|
|
173
227
|
)
|
|
174
228
|
}
|
|
175
229
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
guard let self = self else { return }
|
|
230
|
+
private func toRadians(_ degrees: Double) -> Double {
|
|
231
|
+
return degrees * Double.pi / 180.0
|
|
232
|
+
}
|
|
180
233
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
234
|
+
private func haversine(_ point1: [Double], _ point2: [Double]) -> Double {
|
|
235
|
+
let lon1 = point1[0]
|
|
236
|
+
let lat1 = point1[1]
|
|
237
|
+
let lon2 = point2[0]
|
|
238
|
+
let lat2 = point2[1]
|
|
239
|
+
|
|
240
|
+
let dLat = toRadians(lat2 - lat1)
|
|
241
|
+
let dLon = toRadians(lon2 - lon1)
|
|
242
|
+
|
|
243
|
+
let aaa = sin(dLat / 2) * sin(dLat / 2) +
|
|
244
|
+
cos(toRadians(lat1)) * cos(toRadians(lat2)) *
|
|
245
|
+
sin(dLon / 2) * sin(dLon / 2)
|
|
246
|
+
|
|
247
|
+
let ccc = 2 * atan2(sqrt(aaa), sqrt(1 - aaa))
|
|
248
|
+
|
|
249
|
+
return BackgroundGeolocation.EARTH_RADIUS_M * ccc
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private func distancePointToLineSegment(_ point: [Double], _ lineStart: [Double], _ lineEnd: [Double]) -> Double {
|
|
253
|
+
// Calculate the distances between the three points using Haversine
|
|
254
|
+
let distAB = haversine(point, lineStart)
|
|
255
|
+
let distAC = haversine(point, lineEnd)
|
|
256
|
+
let distBC = haversine(lineStart, lineEnd)
|
|
257
|
+
|
|
258
|
+
// Handle the edge case where the line segment is a single point
|
|
259
|
+
if distBC == 0 {
|
|
260
|
+
return distAB
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Check if the angles at the line segment's endpoints are obtuse.
|
|
264
|
+
// We use the Law of Cosines (c^2 = a^2 + b^2 - 2ab*cos(C))
|
|
265
|
+
// If cos(C) < 0, the angle is obtuse.
|
|
266
|
+
|
|
267
|
+
// Angle at B (lineStart)
|
|
268
|
+
let epsilon = Double.ulpOfOne
|
|
269
|
+
let cosB = (pow(distAB, 2) + pow(distBC, 2) - pow(distAC, 2)) / (2 * distAB * distBC + epsilon)
|
|
270
|
+
if cosB < 0 {
|
|
271
|
+
return distAB
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Angle at C (lineEnd)
|
|
275
|
+
let cosC = (pow(distAC, 2) + pow(distBC, 2) - pow(distAB, 2)) / (2 * distAC * distBC + epsilon)
|
|
276
|
+
if cosC < 0 {
|
|
277
|
+
return distAC
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// If both angles are acute, the closest point is on the line segment itself.
|
|
281
|
+
// We can calculate the distance (height of the triangle) using its area.
|
|
282
|
+
|
|
283
|
+
// 1. Calculate the semi-perimeter of the triangle ABC
|
|
284
|
+
let semi = (distAB + distAC + distBC) / 2
|
|
285
|
+
|
|
286
|
+
// 2. Calculate the area using Heron's formula
|
|
287
|
+
let area = sqrt(max(0, semi * (semi - distAB) * (semi - distAC) * (semi - distBC)))
|
|
288
|
+
|
|
289
|
+
// 3. The distance is the height of the triangle from point A to the base BC
|
|
290
|
+
// Area = 0.5 * base * height => height = 2 * Area / base
|
|
291
|
+
return (2 * area) / (distBC + epsilon)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private func distancePointToRoute(_ point: [Double]) -> Double {
|
|
295
|
+
// If the route has less than 2 points, we can't form a segment.
|
|
296
|
+
if plannedRoute.count < 2 {
|
|
297
|
+
if plannedRoute.count == 1 {
|
|
298
|
+
return haversine(point, plannedRoute[0])
|
|
186
299
|
}
|
|
300
|
+
return Double.infinity // No line segments to measure against
|
|
301
|
+
}
|
|
187
302
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
303
|
+
var minDistance = Double.infinity
|
|
304
|
+
|
|
305
|
+
for pointIndex in 0..<(plannedRoute.count - 1) {
|
|
306
|
+
let lineStart = plannedRoute[pointIndex]
|
|
307
|
+
let lineEnd = plannedRoute[pointIndex + 1]
|
|
308
|
+
let distance = distancePointToLineSegment(point, lineStart, lineEnd)
|
|
309
|
+
if distance < minDistance {
|
|
310
|
+
minDistance = distance
|
|
196
311
|
}
|
|
197
312
|
}
|
|
313
|
+
|
|
314
|
+
return minDistance
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private func checkRouteDeviation(_ location: CLLocation) {
|
|
318
|
+
guard audioPlayer != nil && plannedRoute.count > 0 else { return }
|
|
319
|
+
|
|
320
|
+
let currentPoint = [location.coordinate.longitude, location.coordinate.latitude]
|
|
321
|
+
let offRoute = distancePointToRoute(currentPoint) > distanceThreshold
|
|
322
|
+
|
|
323
|
+
if offRoute && !isOffRoute {
|
|
324
|
+
audioPlayer?.play()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
isOffRoute = offRoute
|
|
198
328
|
}
|
|
199
329
|
|
|
200
330
|
public func locationManager(
|
|
@@ -233,6 +363,7 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
|
|
|
233
363
|
}
|
|
234
364
|
|
|
235
365
|
if isLocationValid(location) {
|
|
366
|
+
checkRouteDeviation(location)
|
|
236
367
|
return call.resolve(formatLocation(location))
|
|
237
368
|
}
|
|
238
369
|
}
|
package/package.json
CHANGED