public void FromTile() { CheapRuler ruler1 = new CheapRuler(50.5); CheapRuler ruler2 = CheapRuler.FromTile(11041, 15); var p1 = new double[] { 30.5, 50.5 }; var p2 = new double[] { 30.51, 50.51 }; Assert.AreEqual(ruler1.Distance(p1, p2), ruler2.Distance(p1, p2), 3e-5, "CheapRuler.FromTile distance"); }
public void DistanceInMiles() { CheapRuler ruler = new CheapRuler(32.8351); CheapRuler rulerMiles = new CheapRuler(32.8351, CheapRulerUnits.Miles); double distKm = ruler.Distance(new double[] { 30.5, 32.8351 }, new double[] { 30.51, 32.8451 }); double distMiles = rulerMiles.Distance(new double[] { 30.5, 32.8351 }, new double[] { 30.51, 32.8451 }); Assert.AreEqual(1.609344, distKm / distMiles, 1e-12, "wrong distance in miles"); }
public void ExtractFootTrace() { CheapRuler ruler = new CheapRuler(_footTrace[0].Latitude); ProbeExtractorOptions options = new ProbeExtractorOptions(); ProbeExtractor extractor = new ProbeExtractor(ruler, options); List <Probe> extractedProbes = extractor.ExtractProbes(_footTrace); Assert.AreEqual(40, extractedProbes.Count); }
public void DistanceInNauticalMiles() { CheapRuler ruler = new CheapRuler(32.8351); CheapRuler rulerMiles = new CheapRuler(32.8351, CheapRulerUnits.Miles); CheapRuler rulerNauticalMiles = new CheapRuler(32.8351, CheapRulerUnits.NauticalMiles); double distKm = ruler.Distance(new double[] { 30.5, 32.8351 }, new double[] { 30.51, 32.8451 }); double distMiles = rulerMiles.Distance(new double[] { 30.5, 32.8351 }, new double[] { 30.51, 32.8451 }); double distNauticalMiles = rulerNauticalMiles.Distance(new double[] { 30.5, 32.8351 }, new double[] { 30.51, 32.8451 }); Debug.LogFormat("{0} {1}", distKm, distNauticalMiles); Assert.AreEqual(1.852, distKm / distNauticalMiles, 1e-12, "wrong distance km vs nautical miles"); Assert.AreEqual(1.15078, distMiles / distNauticalMiles, 1e-6, "wrong distance miles vs nautical miles"); }
public void ExtractProbes() { CheapRuler ruler = CheapRuler.FromTile(49, 7); ProbeExtractorOptions options = new ProbeExtractorOptions() { MinTimeBetweenProbes = 1, // seconds MaxDistanceRatioJump = 3, // do not include probes when the distance is 3 times bigger than the previous one MaxDurationRatioJump = 3, // do not include probes when the duration is 3 times bigger than the previous one MaxAcceleration = 15, // meters per second per second MaxDeceleration = 18 // meters per second per second }; ProbeExtractor extractor = new ProbeExtractor(ruler, options); List <Probe> extractedProbes = extractor.ExtractProbes(_trace); Assert.AreEqual(12, extractedProbes.Count, "12 probes were expected to be extracted"); for (int i = 0; i < extractedProbes.Count; i++) { Probe fp = _probes[i]; // fixture probe Probe ep = extractedProbes[i]; // extracted probe Assert.AreEqual(fp.Longitude, ep.Longitude, 0.001, "probe[" + i.ToString() + "]: longitude doesn't match"); Assert.AreEqual(fp.Latitude, ep.Latitude, 0.001, "probe[" + i.ToString() + "]: latitude doesn't match"); Assert.AreEqual(fp.StartTime, ep.StartTime, "probe[" + i.ToString() + "]: start time doesn't match"); Assert.AreEqual(fp.Duration, ep.Duration, "probe[" + i.ToString() + "]: duration doesn't match"); Assert.AreEqual(fp.Speed, ep.Speed, 0.001, "probe[" + i.ToString() + "]: speed doesn't match"); Assert.AreEqual(fp.Bearing, ep.Bearing, 0.001, "probe[" + i.ToString() + "]: bearing doesn't match"); Assert.AreEqual(fp.Distance, ep.Distance, 0.001, "probe[" + i.ToString() + "]: distance doesn't match"); Assert.AreEqual(fp.IsGood, ep.IsGood, "probe[" + i.ToString() + "]: longitude doesn't match"); } options.MinTimeBetweenProbes = 2; extractor = new ProbeExtractor(ruler, options); extractedProbes = extractor.ExtractProbes(_trace); Assert.AreEqual(5, extractedProbes.Count, "5 probes were expected to be extracted"); options.OutputBadProbes = true; extractor = new ProbeExtractor(ruler, options); extractedProbes = extractor.ExtractProbes(_trace); Assert.AreEqual(13, extractedProbes.Count, "13 probes were expected to be extracted"); }
private void GetDistanceToBin() { playerCurrentLocation = locationProvider.CurrentLocation.LatitudeLongitude; from = playerCurrentLocation.ToArray(); CheapRuler cr = new CheapRuler(from[0], CheapRulerUnits.Meters); for (int i = 0; i < spawnBins.locations.Length; i++) { spawnBins.spawnedObjects[i].GetComponentInChildren <MeshRenderer>().material.color = Color.black; toBinLocation = spawnBins.locations[i]; to = toBinLocation.ToArray(); distances[i] = cr.Distance(from, to); } minIndex = Array.IndexOf(distances, distances.Min()); myText.text = distances[minIndex].ToString(); // Show dist of the closest on ui [for testing] spawnBins.spawnedObjects[minIndex].GetComponentInChildren <MeshRenderer>().material.color = Color.green; //minIndex = GetIndexOfArray(distances, distances.Min()); }
/// <summary> /// Enable location and compass services. /// Sends continuous location and heading updates based on /// _desiredAccuracyInMeters and _updateDistanceInMeters. /// </summary> /// <returns>The location routine.</returns> IEnumerator PollLocationRoutine() { #if UNITY_EDITOR while (!UnityEditor.EditorApplication.isRemoteConnected) { // exit if we are not the selected location provider if (null != LocationProviderFactory.Instance && null != LocationProviderFactory.Instance.DefaultLocationProvider) { if (!this.Equals(LocationProviderFactory.Instance.DefaultLocationProvider)) { yield break; } } Debug.LogWarning("Remote device not connected via 'Unity Remote'. Waiting ..." + Environment.NewLine + "If Unity seems to be stuck here make sure 'Unity Remote' is running and restart Unity with your device already connected."); yield return(_wait1sec); } #endif //request runtime fine location permission on Android if not yet allowed #if UNITY_ANDROID if (!_locationService.isEnabledByUser) { UniAndroidPermission.RequestPermission(AndroidPermission.ACCESS_FINE_LOCATION); //wait for user to allow or deny while (!_gotPermissionRequestResponse) { yield return(_wait1sec); } } #endif if (!_locationService.isEnabledByUser) { Debug.LogError("DeviceLocationProvider: Location is not enabled by user!"); _currentLocation.IsLocationServiceEnabled = false; SendLocation(_currentLocation); yield break; } _currentLocation.IsLocationServiceInitializing = true; _locationService.Start(_desiredAccuracyInMeters, _updateDistanceInMeters); Input.compass.enabled = true; int maxWait = 20; while (_locationService.status == LocationServiceStatus.Initializing && maxWait > 0) { yield return(_wait1sec); maxWait--; } if (maxWait < 1) { Debug.LogError("DeviceLocationProvider: " + "Timed out trying to initialize location services!"); _currentLocation.IsLocationServiceInitializing = false; _currentLocation.IsLocationServiceEnabled = false; SendLocation(_currentLocation); yield break; } if (_locationService.status == LocationServiceStatus.Failed) { Debug.LogError("DeviceLocationProvider: " + "Failed to initialize location services!"); _currentLocation.IsLocationServiceInitializing = false; _currentLocation.IsLocationServiceEnabled = false; SendLocation(_currentLocation); yield break; } _currentLocation.IsLocationServiceInitializing = false; _currentLocation.IsLocationServiceEnabled = true; #if UNITY_EDITOR // HACK: this is to prevent Android devices, connected through Unity Remote, // from reporting a location of (0, 0), initially. yield return(_wait1sec); #endif System.Globalization.CultureInfo invariantCulture = System.Globalization.CultureInfo.InvariantCulture; while (true) { var lastData = _locationService.lastData; var timestamp = lastData.timestamp; /////////////////////////////// // oh boy, Unity what are you doing??? // on some devices it seems that // Input.location.status != LocationServiceStatus.Running // nevertheless new location is available ////////////////////////////// //Debug.LogFormat("Input.location.status: {0}", Input.location.status); _currentLocation.IsLocationServiceEnabled = _locationService.status == LocationServiceStatus.Running || timestamp > _lastLocationTimestamp; _currentLocation.IsUserHeadingUpdated = false; _currentLocation.IsLocationUpdated = false; if (!_currentLocation.IsLocationServiceEnabled) { yield return(_waitUpdateTime); continue; } // device orientation, user heading get calculated below _deviceOrientationSmoothing.Add(Input.compass.trueHeading); _currentLocation.DeviceOrientation = (float)_deviceOrientationSmoothing.Calculate(); //_currentLocation.LatitudeLongitude = new Vector2d(lastData.latitude, lastData.longitude); // HACK to get back to double precision, does this even work? // https://forum.unity.com/threads/precision-of-location-longitude-is-worse-when-longitude-is-beyond-100-degrees.133192/#post-1835164 double latitude = double.Parse(lastData.latitude.ToString("R", invariantCulture), invariantCulture); double longitude = double.Parse(lastData.longitude.ToString("R", invariantCulture), invariantCulture); Vector2d previousLocation = new Vector2d(_currentLocation.LatitudeLongitude.x, _currentLocation.LatitudeLongitude.y); _currentLocation.LatitudeLongitude = new Vector2d(latitude, longitude); _currentLocation.Accuracy = (float)Math.Floor(lastData.horizontalAccuracy); // sometimes Unity's timestamp doesn't seem to get updated, or even jump back in time // do an additional check if location has changed _currentLocation.IsLocationUpdated = timestamp > _lastLocationTimestamp || !_currentLocation.LatitudeLongitude.Equals(previousLocation); _currentLocation.Timestamp = timestamp; _lastLocationTimestamp = timestamp; if (_currentLocation.IsLocationUpdated) { if (_lastPositions.Count > 0) { // only add position if user has moved +1m since we added the previous position to the list CheapRuler cheapRuler = new CheapRuler(_currentLocation.LatitudeLongitude.x, CheapRulerUnits.Meters); Vector2d p = _currentLocation.LatitudeLongitude; double distance = cheapRuler.Distance( new double[] { p.y, p.x }, new double[] { _lastPositions[0].y, _lastPositions[0].x } ); if (distance > 1.0) { _lastPositions.Add(_currentLocation.LatitudeLongitude); } } else { _lastPositions.Add(_currentLocation.LatitudeLongitude); } } // if we have enough positions calculate user heading ourselves. // Unity does not provide bearing based on GPS locations, just // device orientation based on Compass.Heading. // nevertheless, use compass for intial UserHeading till we have // enough values to calculate ourselves. if (_lastPositions.Count < _maxLastPositions) { _currentLocation.UserHeading = _currentLocation.DeviceOrientation; _currentLocation.IsUserHeadingUpdated = true; } else { Vector2d newestPos = _lastPositions[0]; Vector2d oldestPos = _lastPositions[_maxLastPositions - 1]; CheapRuler cheapRuler = new CheapRuler(newestPos.x, CheapRulerUnits.Meters); // distance between last and first position in our buffer double distance = cheapRuler.Distance( new double[] { newestPos.y, newestPos.x }, new double[] { oldestPos.y, oldestPos.x } ); // positions are minimum required distance apart (user is moving), calculate user heading if (distance >= _minDistanceOldestNewestPosition) { float[] lastHeadings = new float[_maxLastPositions - 1]; for (int i = 1; i < _maxLastPositions; i++) { // atan2 increases angle CCW, flip sign of latDiff to get CW double latDiff = -(_lastPositions[i].x - _lastPositions[i - 1].x); double lngDiff = _lastPositions[i].y - _lastPositions[i - 1].y; // +90.0 to make top (north) 0° double heading = (Math.Atan2(latDiff, lngDiff) * 180.0 / Math.PI) + 90.0f; // stay within [0..360]° range if (heading < 0) { heading += 360; } if (heading >= 360) { heading -= 360; } lastHeadings[i - 1] = (float)heading; } _userHeadingSmoothing.Add(lastHeadings[0]); float finalHeading = (float)_userHeadingSmoothing.Calculate(); //fix heading to have 0° for north, 90° for east, 180° for south and 270° for west finalHeading = finalHeading >= 180.0f ? finalHeading - 180.0f : finalHeading + 180.0f; _currentLocation.UserHeading = finalHeading; _currentLocation.IsUserHeadingUpdated = true; } } _currentLocation.TimestampDevice = UnixTimestampUtils.To(DateTime.UtcNow); SendLocation(_currentLocation); yield return(_waitUpdateTime); } }
/// <summary> /// Enable location and compass services. /// Sends continuous location and heading updates based on /// _desiredAccuracyInMeters and _updateDistanceInMeters. /// </summary> /// <returns>The location routine.</returns> IEnumerator PollLocationRoutine() { #if UNITY_EDITOR while (!UnityEditor.EditorApplication.isRemoteConnected) { yield return(null); } #endif //request runtime fine location permission on Android if not yet allowed #if UNITY_ANDROID if (!Input.location.isEnabledByUser) { UniAndroidPermission.RequestPermission(AndroidPermission.ACCESS_FINE_LOCATION); //wait for user to allow or deny while (!_gotPermissionRequestResponse) { yield return(_wait1sec); } } #endif if (!Input.location.isEnabledByUser) { Debug.LogError("DeviceLocationProvider: Location is not enabled by user!"); _currentLocation.IsLocationServiceEnabled = false; SendLocation(_currentLocation); yield break; } _currentLocation.IsLocationServiceInitializing = true; Input.location.Start(_desiredAccuracyInMeters, _updateDistanceInMeters); Input.compass.enabled = true; int maxWait = 20; while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0) { yield return(_wait1sec); maxWait--; } if (maxWait < 1) { Debug.LogError("DeviceLocationProvider: " + "Timed out trying to initialize location services!"); _currentLocation.IsLocationServiceInitializing = false; _currentLocation.IsLocationServiceEnabled = false; SendLocation(_currentLocation); yield break; } if (Input.location.status == LocationServiceStatus.Failed) { Debug.LogError("DeviceLocationProvider: " + "Failed to initialize location services!"); _currentLocation.IsLocationServiceInitializing = false; _currentLocation.IsLocationServiceEnabled = false; SendLocation(_currentLocation); yield break; } _currentLocation.IsLocationServiceInitializing = false; _currentLocation.IsLocationServiceEnabled = true; #if UNITY_EDITOR // HACK: this is to prevent Android devices, connected through Unity Remote, // from reporting a location of (0, 0), initially. yield return(_wait1sec); #endif System.Globalization.CultureInfo invariantCulture = System.Globalization.CultureInfo.InvariantCulture; while (true) { _currentLocation.IsLocationServiceEnabled = Input.location.status == LocationServiceStatus.Running; _currentLocation.IsUserHeadingUpdated = false; _currentLocation.IsLocationUpdated = false; if (!_currentLocation.IsLocationServiceEnabled) { yield return(_waitUpdateTime); continue; } // device orientation, user heading get calculated below _currentLocation.DeviceOrientation = Input.compass.trueHeading; var lastData = Input.location.lastData; var timestamp = lastData.timestamp; //Debug.LogFormat("{0:yyyyMMdd-HHmmss} acc:{1:0.00} {2} / {3}", UnixTimestampUtils.From(timestamp), lastData.horizontalAccuracy, lastData.latitude, lastData.longitude); //_currentLocation.LatitudeLongitude = new Vector2d(lastData.latitude, lastData.longitude); // HACK to get back to double precision, does this even work? // https://forum.unity.com/threads/precision-of-location-longitude-is-worse-when-longitude-is-beyond-100-degrees.133192/#post-1835164 double latitude = double.Parse(lastData.latitude.ToString("R", invariantCulture), invariantCulture); double longitude = double.Parse(lastData.longitude.ToString("R", invariantCulture), invariantCulture); _currentLocation.LatitudeLongitude = new Vector2d(latitude, longitude); _currentLocation.Accuracy = (int)System.Math.Floor(lastData.horizontalAccuracy); _currentLocation.IsLocationUpdated = timestamp > _lastLocationTimestamp; _currentLocation.Timestamp = timestamp; _lastLocationTimestamp = timestamp; if (_currentLocation.IsLocationUpdated) { _lastPositions.Add(_currentLocation.LatitudeLongitude); } // calculate user heading. only if we have enough positions available if (_lastPositions.Count < _maxLastPositions) { _currentLocation.UserHeading = 0; } else { Vector2d newestPos = _lastPositions[0]; Vector2d oldestPos = _lastPositions[_maxLastPositions - 1]; CheapRuler cheapRuler = new CheapRuler(newestPos.x, CheapRulerUnits.Meters); // distance between last and first position in our buffer double distance = cheapRuler.Distance( new double[] { newestPos.y, newestPos.x }, new double[] { oldestPos.y, oldestPos.x } ); // positions are minimum required distance apart (user is moving), calculate user heading if (distance >= _minDistanceOldestNewestPosition) { // calculate final heading from last positions but give newest headings more weight: // '_lastPositions' contains newest at [0] // formula: // (heading[0] * e^weight[0] + heading[1] * e^weight[1] + .... ) / weight sum float[] lastHeadings = new float[_maxLastPositions - 1]; float[] actualWeights = new float[_maxLastPositions - 1]; float finalHeading = 0f; for (int i = 1; i < _maxLastPositions; i++) { lastHeadings[i - 1] = (float)(Math.Atan2(_lastPositions[i].y - _lastPositions[i - 1].y, _lastPositions[i].x - _lastPositions[i - 1].x) * 180 / Math.PI); // quick fix to take care of 355° and 5° being apart 10° and not 350° if (lastHeadings[i - 1] > 180) { lastHeadings[i - 1] -= 360f; } } for (int i = 0; i < lastHeadings.Length; i++) { actualWeights[i] = (float)Math.Exp(_headingWeights[i]); finalHeading += lastHeadings[i] * actualWeights[i]; } float weightSum = actualWeights.Sum(); finalHeading /= weightSum; // stay within [0..359] no negative angles if (finalHeading < 0) { finalHeading += 360; } _currentLocation.UserHeading = finalHeading; _currentLocation.IsUserHeadingUpdated = true; } } SendLocation(_currentLocation); yield return(_waitUpdateTime); } }
/// <summary> /// /// </summary> /// <param name="ruler">A CheapRuler instance, expected in kilometers.</param> /// <param name="options"></param> public ProbeExtractor(CheapRuler ruler, ProbeExtractorOptions options) { _ruler = ruler; _options = options; }
protected void Build(VectorFeatureUnity feature, UnityTile tile, GameObject parent) { pos = GameManager.poiLocaitonList; typeDict = GameManager.poiTypeList; if (feature.Properties.ContainsKey("extrude") && !Convert.ToBoolean(feature.Properties["extrude"])) { return; } if (feature.Points.Count < 1) { return; } //this will be improved in next version and will probably be replaced by filters var styleSelectorKey = _layerProperties.coreOptions.sublayerName; if (styleSelectorKey != "Buildings") { // Calculate Distance between POIs and do not spawn if too close to other POIs var rangeBool = true; var geom = feature.Data.Geometry <float>(); var location = feature.Data.GeometryAsWgs84((ulong)tile.CanonicalTileId.Z, (ulong)tile.CanonicalTileId.X, (ulong)tile.CanonicalTileId.Y)[0][0]; var locationDouble = new double[] { location.Lat, location.Lng }; var maxDistance = 150.0f; CheapRuler cr = new CheapRuler(locationDouble[1], CheapRulerUnits.Meters); // If spawned object is treasure only check distance to other treasures if (styleSelectorKey == "Treasures") { foreach (KeyValuePair <string, double[]> position in pos) { if (position.Key.Contains("Treasures")) { if (cr.Distance(locationDouble, position.Value) < maxDistance && !pos.ContainsKey(styleSelectorKey + " - " + feature.Data.Id.ToString())) { rangeBool = false; return; } } } } // for POIs check only distance to POIs and not to treasures else { foreach (KeyValuePair <string, double[]> position in pos) { if (!position.Key.Contains("Treasures")) { if (cr.Distance(locationDouble, position.Value) < maxDistance && !pos.ContainsKey(styleSelectorKey + " - " + feature.Data.Id.ToString())) { rangeBool = false; return; } } } } // Add spawned POIs and treasures to dictionaries if (rangeBool && feature.Data.Id.ToString() != null && !pos.ContainsKey(styleSelectorKey + " - " + feature.Data.Id.ToString())) { pos.Add(styleSelectorKey + " - " + feature.Data.Id.ToString(), locationDouble); typeDict.Add(styleSelectorKey + " - " + feature.Data.Id.ToString(), feature.Properties); } } var meshData = new MeshData(); meshData.TileRect = tile.Rect; //and finally, running the modifier stack on the feature var processed = false; if (!processed) { if (_defaultStack != null) { _defaultStack.Execute(tile, feature, meshData, parent, styleSelectorKey); } } }