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 : IComparer
{
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;
}
}
Proximity Search done.
No comments:
Post a Comment