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");
        }
Пример #2
0
        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");
        }
Пример #6
0
    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);
            }
        }
Пример #9
0
 /// <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);
                }
            }
        }