using System;
using System.Collections.Generic;
using UnityEngine;
using LastPositionItem = OnlineMapsLocationService.LastPositionItem;
///
/// Smoothes changes in GPS location
///
public class SmoothGPSMovement: MonoBehaviour
{
///
/// Number of seconds to correct position
///
public float correctionTime = 0.5f;
///
/// Should the map position be updated?
///
public bool updateMapPosition = true;
///
/// Smooth changes in compass values
///
public bool lerpCompassValue = true;
///
/// Reference to Location Service
///
private OnlineMapsLocationService locationService;
///
/// Current speed (km/h)
///
private float speed;
///
/// Compass true heading (degree)
///
private float compass;
///
/// Last known location
///
private OnlineMapsVector2d lastKnownLocation;
///
/// Current simulated location
///
private OnlineMapsVector2d currentLocation;
///
/// Correction vector
///
private OnlineMapsVector2d correction;
///
/// Reference to the marker
///
private OnlineMapsMarker marker;
///
/// List of last positions
///
private List lastPositions;
///
/// The maximum number of items in the list of last positions
///
private int maxPositionCount = 3;
///
/// Progress of correction
///
private float correctionProgress;
///
/// Smoothed compass value (degree)
///
private float smoothedCompass;
private void Start()
{
locationService = OnlineMapsLocationService.instance;
if (locationService == null)
{
Debug.LogError("Cannot find Location Service");
Destroy(this);
return;
}
// Subscribe to location events
locationService.OnLocationInited += OnLocationInited;
locationService.OnLocationChanged += OnLocationChanged;
locationService.OnCompassChanged += OnCompassChanged;
}
private void OnLocationInited()
{
// Create the marker and store initial values
marker = OnlineMapsMarkerManager.CreateItem(locationService.position, "Smoothed Marker");
lastKnownLocation = currentLocation = locationService.position;
}
private void OnCompassChanged(float newValue)
{
// Update compass value
compass = newValue * 360;
// If the marker rotation should not smooth, update the rotation
if (!lerpCompassValue && marker != null) marker.rotation = newValue;
}
private void OnLocationChanged(Vector2 newLocation)
{
// Save a new location
lastKnownLocation = newLocation;
// Calculating the correction vector
correction = lastKnownLocation - currentLocation;
// Update current speed
UpdateSpeed();
// Calculate a distance between new and old locations
double dx, dy;
OnlineMapsUtils.DistanceBetweenPoints(newLocation.x, newLocation.y, currentLocation.x, currentLocation.y, out dx, out dy);
double d = Math.Sqrt(dx * dx + dy * dy);
// If the distance is too long or the speed is too low, update the location
if (d > 0.01 || speed < 1)
{
currentLocation = lastKnownLocation;
correction = OnlineMapsVector2d.zero;
}
// Reset correction progress
correctionProgress = 0;
}
private void Update()
{
if (speed < 1 || marker == null) return;
// Smooth changes of compass
if (lerpCompassValue)
{
if (compass - smoothedCompass > 180) smoothedCompass += 360;
else if (compass - smoothedCompass < -180) smoothedCompass -= 360;
if (Math.Abs(compass - smoothedCompass) >= float.Epsilon)
{
if (Mathf.Abs(compass - smoothedCompass) < 0.003f) smoothedCompass = compass;
else smoothedCompass = Mathf.Lerp(smoothedCompass, compass, 0.02f);
marker.rotationDegree = smoothedCompass;
}
}
// Find the expected location
float coveredDistance = Time.deltaTime * speed / 3600f;
double lng, lat;
OnlineMapsUtils.GetCoordinateInDistance(currentLocation.x, currentLocation.y, coveredDistance, compass, out lng, out lat);
currentLocation.x = lng;
currentLocation.y = lat;
// If correction is required, do it
if (correctionProgress < 1 && correction.SqrMagnitude() > 0)
{
float nextCorrectionProgress = correctionProgress + Time.deltaTime / correctionTime;
if (nextCorrectionProgress > 1) nextCorrectionProgress = 1;
float correctionDelta = nextCorrectionProgress - correctionProgress;
currentLocation.x += correction.x * correctionDelta;
currentLocation.y += correction.y * correctionDelta;
correctionProgress = nextCorrectionProgress;
}
// Set marker position
marker.SetPosition(lng, lat);
// Set map position
if (updateMapPosition) OnlineMaps.instance.SetPosition(lng, lat);
}
///
/// Update speed
///
public void UpdateSpeed()
{
if (lastPositions == null) lastPositions = new List();
lastPositions.Add(new LastPositionItem((float)lastKnownLocation.x, (float)lastKnownLocation.y, Time.time));
while (lastPositions.Count > maxPositionCount) lastPositions.RemoveAt(0);
if (lastPositions.Count < 2)
{
speed = 0;
return;
}
LastPositionItem p1 = lastPositions[0];
LastPositionItem p2 = lastPositions[lastPositions.Count - 1];
double dx, dy;
OnlineMapsUtils.DistanceBetweenPoints(p1.lng, p1.lat, p2.lng, p2.lat, out dx, out dy);
double distance = Math.Sqrt(dx * dx + dy * dy);
double time = (p2.timestamp - p1.timestamp) / 3600;
speed = Mathf.Abs((float)(distance / time));
}
}