Proximity search using standard EPiServer functionality is a lot easier since we can do numeric range searches with FindPagesByCriteria. All we need is a helper class which does the geo-stuff for you.
First, the class:
public class GeoPoint { public enum DistanceMeasurement : int { Miles = 0, Kilometers = 1 } public const double EarthRadiusInMiles = 3956.0; public const double EarthRadiusInKilometers = 6367.0; public static double ToRadian(double val) { return val * (Math.PI / 180); } public const double KILOMETERS_PER_DEGREE = 111.3171; private double _longitude; private double _latitude; public GeoPoint(double latitude, double longitude) { _longitude = longitude; _latitude = latitude; } public void GetProximityBoundingBox(double distanceInKm, ref GeoPoint topLeft, ref GeoPoint bottomRight) { double spreadOnLongitude = distanceInKm / GeoPoint.CalculateKilometersPerLongitudeDegree(this.Latitude); double spreadOnLatitude = distanceInKm / GeoPoint.KILOMETERS_PER_DEGREE; // Get top left bounding box point topLeft = new GeoPoint(this.Latitude - spreadOnLatitude, this.Longitude - spreadOnLongitude); // Get bottom right bounding box point bottomRight = new GeoPoint(this.Latitude + spreadOnLatitude, this.Longitude + spreadOnLongitude); } public double Longitude { get { return _longitude; } set { _longitude = value; } } public double Latitude { get { return _latitude; } set { _latitude = value; } } public static double DiffRadian(double val1, double val2) { return ToRadian(val2) - ToRadian(val1); } public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, DistanceMeasurement m) { double radius = EarthRadiusInMiles; if (m == DistanceMeasurement.Kilometers) { radius = EarthRadiusInKilometers; } return radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2)) / 2.0), 2.0) + Math.Cos(ToRadian(lat1)) * Math.Cos(ToRadian(lat2)) * Math.Pow(Math.Sin((DiffRadian(lng1, lng2)) / 2.0), 2.0))))); } public double DistanceFrom(GeoPoint anotherPoint) { return CalcDistance(anotherPoint.Latitude, anotherPoint.Longitude, this.Latitude, this.Longitude, DistanceMeasurement.Kilometers); } public static double CalculateKilometersPerLongitudeDegree(double latitude) { return KILOMETERS_PER_DEGREE; } }Next, the search code.
GeoPoint origin = new GeoPoint(centerLatitude, centerLongitude); // Get top left bounding box point GeoPoint topLeft = null; // Get bottom right bounding box point GeoPoint bottomRight = null; origin.GetProximityBoundingBox(distanceKms, ref topLeft, ref bottomRight); int pageTypeID = PageTypeResolver.Instance.GetPageTypeID(typeof(Club)).Value; // Create criteria collection var criterias = new PropertyCriteriaCollection { // Find pages of a specific page type new PropertyCriteria() { Name = "PageTypeID", Condition = CompareCondition.Equal, Required = true, Type = PropertyDataType.PageType, Value = pageTypeID.ToString() // Page type ID = 5 }, // Greater than Longitude new PropertyCriteria() { Name = "PlaceLongitude", Condition = CompareCondition.GreaterThan, Required = true, Type = PropertyDataType.FloatNumber, Value = topLeft.Longitude.ToString() // Long - Top Left }, // Less than Longitude new PropertyCriteria() { Name = "PlaceLongitude", Condition = CompareCondition.LessThan, Required = true, Type = PropertyDataType.FloatNumber, Value = bottomRight.Longitude.ToString() // Long - Bottom Right }, // Greater than Latitude new PropertyCriteria() { Name = "PlaceLatitude", Condition = CompareCondition.GreaterThan, Required = true, Type = PropertyDataType.FloatNumber, Value = topLeft.Latitude.ToString() // Latitude - Top Left }, // Less than Latitude new PropertyCriteria() { Name = "PlaceLatitude", Condition = CompareCondition.LessThan, Required = true, Type = PropertyDataType.FloatNumber, Value = bottomRight.Latitude.ToString() // Latitude - Bottom Right } }; // Search pages under the start page var pages = DataFactory.Instance.FindPagesWithCriteria(new PageReference(666), criterias);And finally, the sort by distance using a custom sort implementation.
pages.Sort(new DistanceFromOriginSort(origin)); class DistanceFromOriginSort : IComparerProximity Search done.{ private GeoPoint _origin; public DistanceFromOriginSort(GeoPoint origin) { _origin = origin; } public int Compare(PageData x, PageData y) { double xLat = Convert.ToDouble(x.Property["PlaceLatitude"].Value); double xLon = Convert.ToDouble(x.Property["PlaceLongitude"].Value); double yLat = Convert.ToDouble(y.Property["PlaceLatitude"].Value); double yLon = Convert.ToDouble(y.Property["PlaceLongitude"].Value); GeoPoint geoPointX = new GeoPoint(xLat, xLon); GeoPoint geoPointY = new GeoPoint(yLat, yLon); double distance1 = _origin.DistanceFrom(geoPointX); double distance2 = _origin.DistanceFrom(geoPointY); if (distance1 == distance2) { return 0; } if (distance1 < distance2) { return -1; } return 1; } }
No comments:
Post a Comment