コード例 #1
0
        /// <summary>
        /// Checks whether the server is the nearest server to a given location.
        /// </summary>
        /// <param name="TargetLocation">Target GPS location.</param>
        /// <param name="IgnoreServerIds">List of network IDs that should be ignored.</param>
        /// <param name="CloserServerId">If the result is false, this is filled with network identifier of a neighbor server that is closer to the target location.</param>
        /// <param name="Threshold">Optionally, threshold value which allows the function to return true even if there exists a neighbor server that is actually closer
        /// to the target location, but it is only slightly closer than the proximity server.</param>
        /// <returns>true if the server is the nearest proximity server to the target location, false if the server knows at least one other server that is closer
        /// or if the function fails.</returns>
        public async Task <bool> IsServerNearestToLocationAsync(GpsLocation TargetLocation, List <byte[]> IgnoreServerIds, StrongBox <byte[]> CloserServerId, double?Threshold = null)
        {
            log.Trace("(TargetLocation:[{0}],IgnoreServerIds:'{1}',Threshold:{2})", TargetLocation, string.Join(",", IgnoreServerIds), Threshold != null ? Threshold.Value.ToString(CultureInfo.InvariantCulture) : "null");

            bool res = true;

            LocationBasedNetwork loc        = (LocationBasedNetwork)Base.ComponentDictionary[LocationBasedNetwork.ComponentName];
            GpsLocation          myLocation = loc.Location;
            double myDistance = myLocation.DistanceTo(TargetLocation);

            log.Trace("Server's distance to the activity location is {0} metres.", myDistance.ToString(CultureInfo.InvariantCulture));

            double thresholdCoef = 1;

            if (Threshold != null)
            {
                thresholdCoef += Threshold.Value;
            }

            HashSet <byte[]> ignoredServersIds = new HashSet <byte[]>(IgnoreServerIds, StructuralEqualityComparer <byte[]> .Default);

            try
            {
                // We only consider neighbors that finished initialization process.
                // Other nodes might be new and not synced yet with their neighborhood.
                List <Neighbor> allNeighbors = (await GetAsync(n => n.Initialized == true, null, true)).ToList();
                foreach (Neighbor neighbor in allNeighbors)
                {
                    if (ignoredServersIds.Contains(neighbor.NetworkId))
                    {
                        continue;
                    }

                    GpsLocation neighborLocation           = new GpsLocation(neighbor.LocationLatitude, neighbor.LocationLongitude);
                    double      neighborDistance           = neighborLocation.DistanceTo(TargetLocation);
                    double      thresholdNeighborDistance  = neighborDistance * thresholdCoef;
                    bool        serverNearestWithThreshold = myDistance <= thresholdNeighborDistance;
                    if (!serverNearestWithThreshold)
                    {
                        CloserServerId.Value = neighbor.NetworkId;
                        log.Debug("Server network ID '{0}', GPS location [{1}] is closer (distance {2}m, {3}m with threshold) to the target location [{4}] than the current server location [{5}] (distance {6}m).",
                                  neighbor.NetworkId.ToHex(), neighborLocation, neighborDistance.ToString(CultureInfo.InvariantCulture), thresholdNeighborDistance.ToString(CultureInfo.InvariantCulture), TargetLocation,
                                  myLocation, myDistance.ToString(CultureInfo.InvariantCulture));
                        res = false;
                        break;
                    }
                }
            }
            catch (Exception e)
            {
                log.Error("Exception occurred: {0}", e.ToString());
            }

            log.Trace("(-):{0}", res);
            return(res);
        }
コード例 #2
0
        /// <summary>
        /// Creates basic filter expression for location. This filter is not precise filter,
        /// it will just filter out the majority of the identities that the caller is not interested it.
        /// </summary>
        /// <param name="LocationFilter">GPS location of the target area centre.</param>
        /// <param name="Radius">Target area radius in metres.</param>
        /// <returns>Filter expression for the database query.</returns>
        public static Expression <Func <Q, bool> > GetLocationFilterExpression <Q>(GpsLocation LocationFilter, uint Radius) where Q : IdentityBase
        {
            log.Trace("(LocationFilter:'{0:US}',Radius:{1})", LocationFilter, Radius);
            Expression <Func <Q, bool> > res = null;

            // There are several separated cases:
            //  1) Radius is very large - i.e. greater than 5,000 km. In this case, we do no filtering on this level at all.
            //  2) Distance of the target area centre to one of the poles is not larger than the radius. In this case, we calculate latitude and longitude ranges,
            //     from which we then construct a target rectangle on the sphere that will represent our target area of interest.
            //  3) Distance of the target area centre to one of the poles is larger than the radius. In this case, we only set some of the boundaries,
            //     but we will not have a full rectangle on the sphere. There are several subcases here described below.


            // 1) Radius is very large, no filtering.
            if (Radius > 5000000)
            {
                log.Trace("(-)[LARGE_RADIUS]");
                return(res);
            }

            GpsLocation northPole = new GpsLocation(90.0m, 0.0m);
            GpsLocation southPole = new GpsLocation(-90.0m, 0.0m);

            double northPoleDistance = LocationFilter.DistanceTo(northPole);
            double southPoleDistance = LocationFilter.DistanceTo(southPole);

            double radius = (double)Radius;

            if (radius >= northPoleDistance)
            {
                // 2) Distance to pole is not larger than the radius:
                //    a) North Pole
                //
                // In this case we go to the South from the centre to find the minimal latitude
                // and there will be no limit on longitude.
                GpsLocation minLatitudeLocation = LocationFilter.GoVector(GpsLocation.BearingSouth, radius);
                log.Trace("Radius >= North Pole Distance, min latitude is {0}.", minLatitudeLocation.Latitude);
                res = i => i.InitialLocationLatitude >= minLatitudeLocation.Latitude;
            }
            else if (radius >= southPoleDistance)
            {
                // 2) Distance to pole is not larger than the radius:
                //    b) South Pole
                //
                // In this case we go to the North from the centre to find the maximal latitude.
                // and there will be no limit on longitude.
                GpsLocation maxLatitudeLocation = LocationFilter.GoVector(GpsLocation.BearingNorth, radius);
                log.Trace("Radius >= South Pole Distance, max latitude is {0}.", maxLatitudeLocation.Latitude);
                res = i => i.InitialLocationLatitude <= maxLatitudeLocation.Latitude;
            }
            else
            {
                // 3) Distance to poles is larger than the radius.
                //
                // In this case we create a rectangle on the sphere, in which the target identities are expected to be.
                // Using this square we will find latitude and longitude ranges for the database query.

                // Find a GPS square that contains the whole target circle area.
                GpsSquare square = LocationFilter.GetSquare((double)Radius);

                // Get latitude range - this is simple, left-top and right-top corners define the max latitude,
                // and left-bottom and right-bottom corners define the min latitude.
                decimal maxLatitude = square.MidTop.Latitude;
                decimal minLatitude = square.MidBottom.Latitude;
                log.Trace("GPS square is {0:US}, min latitude is {1}, max latitude is {2}.", square, minLatitude, maxLatitude);

                // Get longitude range - we have to examine all four corners here as it depends on which hemisphere they are
                // and there are several different cases due to possibility of crossing longitude 180.

                bool leftCornersSameSign  = Math.Sign(square.LeftBottom.Longitude) == Math.Sign(square.LeftTop.Longitude);
                bool rightCornersSameSign = Math.Sign(square.RightBottom.Longitude) == Math.Sign(square.RightTop.Longitude);

                if (leftCornersSameSign && rightCornersSameSign && (Math.Sign(square.LeftTop.Longitude) == Math.Sign(square.RightTop.Longitude)))
                {
                    // a) Square does not cross longitude 180. This case is simple, we find left most and right most longitudes
                    // and our target profiles has to be between those two.
                    decimal leftLongitude  = Math.Min(square.LeftTop.Longitude, square.LeftBottom.Longitude);
                    decimal rightLongitude = Math.Max(square.RightTop.Longitude, square.RightBottom.Longitude);

                    log.Trace("Square does not cross lon 180. left longitude is {0}, right longitude is {1}.", leftLongitude, rightLongitude);
                    res = i => (minLatitude <= i.InitialLocationLatitude) && (i.InitialLocationLatitude <= maxLatitude) &&
                          (leftLongitude <= i.InitialLocationLongitude) && (i.InitialLocationLongitude <= rightLongitude);
                }
                else
                {
                    decimal leftLongitude;
                    decimal rightLongitude;

                    // b) Square crosses longitude 180. This is the more complicated case. One or two corners
                    // have positive longitude and the remaining have negative longitude.
                    if (leftCornersSameSign)
                    {
                        // Left top and left bottom corners are on the same side of longitude 180.
                        // The left most corner is the one with smaller longitude value.
                        leftLongitude = Math.Min(square.LeftTop.Longitude, square.LeftBottom.Longitude);
                    }
                    else
                    {
                        // Left top and left bottom corners are NOT on the same side of longitude 180.
                        // The left most corner is the one with the positive value as the negative value is on the right of longitude 180.
                        leftLongitude = square.LeftTop.Longitude > 0 ? square.LeftTop.Longitude : square.LeftBottom.Longitude;
                    }

                    if (rightCornersSameSign)
                    {
                        // Right top and right bottom corners are on the same side of longitude 180.
                        // The right most corner is the one with higher longitude value.
                        rightLongitude = Math.Max(square.RightTop.Longitude, square.RightBottom.Longitude);
                    }
                    else
                    {
                        // Right top and right bottom corners are NOT on the same side of longitude 180.
                        // The right most corner is the one with the negative value as the positive value is on the left of longitude 180.
                        rightLongitude = square.RightTop.Longitude < 0 ? square.RightTop.Longitude : square.RightBottom.Longitude;
                    }

                    // Note the OR operator instead of AND operator for longitude comparison.
                    // This is because a longitude value can not be higher than e.g. 170 and lower than e.g. -150 at the same time.
                    // The point is within the square if its longitude is 170 or more (up to 180) OR -150 or less (down to -180).
                    log.Trace("Square crosses lon 180. left longitude is {0}, right longitude is {1}.", leftLongitude, rightLongitude);
                    res = i => (minLatitude <= i.InitialLocationLatitude) && (i.InitialLocationLatitude <= maxLatitude) &&
                          ((leftLongitude <= i.InitialLocationLongitude) || (i.InitialLocationLongitude <= rightLongitude));
                }
            }

            log.Trace("(-)");
            return(res);
        }