@capgo/background-geolocation 7.0.10 → 7.0.12
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 +144 -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,50 @@ 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
|
+
guard let soundFile = call.getString("soundFile") else {
|
|
163
|
+
call.reject("Sound file is required")
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let routeArray = call.getArray("route", Any.self) ?? []
|
|
168
|
+
var route: [[Double]] = []
|
|
169
|
+
|
|
170
|
+
for routePoint in routeArray {
|
|
171
|
+
if let pointArray = routePoint as? [Double], pointArray.count == 2 {
|
|
172
|
+
route.append(pointArray)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let distance = call.getDouble("distance") ?? 50.0
|
|
177
|
+
|
|
178
|
+
let assetPath = "public/" + soundFile
|
|
179
|
+
let assetPathSplit = assetPath.components(separatedBy: ".")
|
|
180
|
+
guard let url = Bundle.main.url(forResource: assetPathSplit[0], withExtension: assetPathSplit[1]) else {
|
|
181
|
+
call.reject("Sound file not found: \(assetPath)")
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
do {
|
|
186
|
+
self.audioPlayer?.stop()
|
|
187
|
+
self.audioPlayer = nil
|
|
188
|
+
self.audioPlayer = try AVAudioPlayer(contentsOf: url)
|
|
189
|
+
|
|
190
|
+
// Store route configuration
|
|
191
|
+
self.plannedRoute = route
|
|
192
|
+
self.distanceThreshold = distance
|
|
193
|
+
self.isOffRoute = true
|
|
194
|
+
|
|
195
|
+
call.resolve()
|
|
196
|
+
} catch {
|
|
197
|
+
call.reject("Could not load the sound file: \(error.localizedDescription)")
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
152
202
|
private func startUpdatingLocation() {
|
|
153
203
|
// Avoid unnecessary calls to startUpdatingLocation, which can
|
|
154
204
|
// result in extraneous invocations of didFailWithError.
|
|
@@ -173,28 +223,104 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
|
|
|
173
223
|
)
|
|
174
224
|
}
|
|
175
225
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
guard let self = self else { return }
|
|
226
|
+
private func toRadians(_ degrees: Double) -> Double {
|
|
227
|
+
return degrees * Double.pi / 180.0
|
|
228
|
+
}
|
|
180
229
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
230
|
+
private func haversine(_ point1: [Double], _ point2: [Double]) -> Double {
|
|
231
|
+
let lon1 = point1[0]
|
|
232
|
+
let lat1 = point1[1]
|
|
233
|
+
let lon2 = point2[0]
|
|
234
|
+
let lat2 = point2[1]
|
|
235
|
+
|
|
236
|
+
let dLat = toRadians(lat2 - lat1)
|
|
237
|
+
let dLon = toRadians(lon2 - lon1)
|
|
238
|
+
|
|
239
|
+
let aaa = sin(dLat / 2) * sin(dLat / 2) +
|
|
240
|
+
cos(toRadians(lat1)) * cos(toRadians(lat2)) *
|
|
241
|
+
sin(dLon / 2) * sin(dLon / 2)
|
|
242
|
+
|
|
243
|
+
let ccc = 2 * atan2(sqrt(aaa), sqrt(1 - aaa))
|
|
244
|
+
|
|
245
|
+
return BackgroundGeolocation.EARTH_RADIUS_M * ccc
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private func distancePointToLineSegment(_ point: [Double], _ lineStart: [Double], _ lineEnd: [Double]) -> Double {
|
|
249
|
+
// Calculate the distances between the three points using Haversine
|
|
250
|
+
let distAB = haversine(point, lineStart)
|
|
251
|
+
let distAC = haversine(point, lineEnd)
|
|
252
|
+
let distBC = haversine(lineStart, lineEnd)
|
|
253
|
+
|
|
254
|
+
// Handle the edge case where the line segment is a single point
|
|
255
|
+
if distBC == 0 {
|
|
256
|
+
return distAB
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check if the angles at the line segment's endpoints are obtuse.
|
|
260
|
+
// We use the Law of Cosines (c^2 = a^2 + b^2 - 2ab*cos(C))
|
|
261
|
+
// If cos(C) < 0, the angle is obtuse.
|
|
262
|
+
|
|
263
|
+
// Angle at B (lineStart)
|
|
264
|
+
let epsilon = Double.ulpOfOne
|
|
265
|
+
let cosB = (pow(distAB, 2) + pow(distBC, 2) - pow(distAC, 2)) / (2 * distAB * distBC + epsilon)
|
|
266
|
+
if cosB < 0 {
|
|
267
|
+
return distAB
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Angle at C (lineEnd)
|
|
271
|
+
let cosC = (pow(distAC, 2) + pow(distBC, 2) - pow(distAB, 2)) / (2 * distAC * distBC + epsilon)
|
|
272
|
+
if cosC < 0 {
|
|
273
|
+
return distAC
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// If both angles are acute, the closest point is on the line segment itself.
|
|
277
|
+
// We can calculate the distance (height of the triangle) using its area.
|
|
278
|
+
|
|
279
|
+
// 1. Calculate the semi-perimeter of the triangle ABC
|
|
280
|
+
let semi = (distAB + distAC + distBC) / 2
|
|
281
|
+
|
|
282
|
+
// 2. Calculate the area using Heron's formula
|
|
283
|
+
let area = sqrt(max(0, semi * (semi - distAB) * (semi - distAC) * (semi - distBC)))
|
|
284
|
+
|
|
285
|
+
// 3. The distance is the height of the triangle from point A to the base BC
|
|
286
|
+
// Area = 0.5 * base * height => height = 2 * Area / base
|
|
287
|
+
return (2 * area) / (distBC + epsilon)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private func distancePointToRoute(_ point: [Double]) -> Double {
|
|
291
|
+
// If the route has less than 2 points, we can't form a segment.
|
|
292
|
+
if plannedRoute.count < 2 {
|
|
293
|
+
if plannedRoute.count == 1 {
|
|
294
|
+
return haversine(point, plannedRoute[0])
|
|
186
295
|
}
|
|
296
|
+
return Double.infinity // No line segments to measure against
|
|
297
|
+
}
|
|
187
298
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
299
|
+
var minDistance = Double.infinity
|
|
300
|
+
|
|
301
|
+
for pointIndex in 0..<(plannedRoute.count - 1) {
|
|
302
|
+
let lineStart = plannedRoute[pointIndex]
|
|
303
|
+
let lineEnd = plannedRoute[pointIndex + 1]
|
|
304
|
+
let distance = distancePointToLineSegment(point, lineStart, lineEnd)
|
|
305
|
+
if distance < minDistance {
|
|
306
|
+
minDistance = distance
|
|
196
307
|
}
|
|
197
308
|
}
|
|
309
|
+
|
|
310
|
+
return minDistance
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private func checkRouteDeviation(_ location: CLLocation) {
|
|
314
|
+
guard audioPlayer != nil && plannedRoute.count > 0 else { return }
|
|
315
|
+
|
|
316
|
+
let currentPoint = [location.coordinate.longitude, location.coordinate.latitude]
|
|
317
|
+
let offRoute = distancePointToRoute(currentPoint) > distanceThreshold
|
|
318
|
+
|
|
319
|
+
if offRoute && !isOffRoute {
|
|
320
|
+
audioPlayer?.play()
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
isOffRoute = offRoute
|
|
198
324
|
}
|
|
199
325
|
|
|
200
326
|
public func locationManager(
|
|
@@ -233,6 +359,7 @@ public class BackgroundGeolocation: CAPPlugin, CLLocationManagerDelegate {
|
|
|
233
359
|
}
|
|
234
360
|
|
|
235
361
|
if isLocationValid(location) {
|
|
362
|
+
checkRouteDeviation(location)
|
|
236
363
|
return call.resolve(formatLocation(location))
|
|
237
364
|
}
|
|
238
365
|
}
|
package/package.json
CHANGED