Сценарий =
1) Офисы доставки, расположенные по всей территории США, каждый указывает свой собственный максимальный радиус доставки в милях.
2) Целевой адрес, преобразованный в LatLng, является местом доставки.
Цель = вернуть набор данных отделений доставки (1), предел радиуса доставки которых находится в пределах этого расстояния до целевого адреса (2).
Попытка =
В качестве отправной точки для решения своей проблемы я использую отличный рабочий пример консалтинговой компании Storm по определению ближайшего к клиенту офиса: Хаверсинус расстояние между двумя точками
Моя таблица «Офисы» хранит адрес офиса вместе с их значениями широты и долготы, а также их максимальное расстояние «deliveryLimit».
SQL для вычисления хаверсинуса поражает меня и в настоящее время находится за пределами моего понимания!
Storm SQL приведен ниже, но вместо того, чтобы выбирать только одну строку из прямых вычислений расстояния, мне нужно выбрать все строки офисов, для которых максимальное расстояние доставки составляет меньше, чем расстояние между офисом и заказчиком.
Вопрос1 = Как мне добавить фильтр ограничения максимального расстояния в запрос SQL, чтобы он возвращал офисы с зоной доставки, включающей целевое местоположение?
Вопрос2 = Как я могу ограничить количество запрашиваемых офисов теми, которые реально могут находиться в целевой области США? Например, если целевым местоположением является Бойсе, штат Айдахо, а офисы в Лос-Анджелесе, штат Калифорния, имеют ограничение на дальность доставки в 300 миль. Нет смысла даже опрашивать такие конторы. Однако офисы в Вашингтоне; Штаты Орегон и Северная Невада, граничащие с Айдахо, должны быть включены в поисковый запрос, поскольку некоторые из них могут иметь максимальные значения расстояния, которые достигаются в этом примере Бойсе, ID.
SQL для Haversine, используемый Storm:
SELECT TOP 1 *, ( 3960 * acos( cos( radians( @custLat ) ) *
cos( radians( Lat ) ) * cos( radians( Lng ) - radians( @custLng ) ) +
sin( radians( @custLat ) ) * sin( radians( Lat ) ) ) ) AS Distance
FROM Offices
ORDER BY Distance ASC
В приведенном выше примере SQL выбирается только ближайший к целевой широте / долготе офис (@custLng)
Шторм подошел к расчету дистанции с двух разных сторон. Приведенный выше SQL был первым. Во-вторых, координаты офиса сохраняются в списке в памяти и создается метод с функцией для обхода списка, вычисляя расстояния и, наконец, выбирая ближайший, например:
/// <summary>
/// Returns the distance in miles or kilometers of any two
/// latitude / longitude points.
/// </summary>
/// <param name="pos1">Location 1</param>
/// <param name="pos2">Location 2</param>
/// <param name="unit">Miles or Kilometers</param>
/// <returns>Distance in the requested unit</returns>
public double HaversineDistance(LatLng pos1, LatLng pos2, DistanceUnit unit)
{
double R = (unit == DistanceUnit.Miles) ? 3960 : 6371;
var lat = (pos2.Latitude - pos1.Latitude).ToRadians();
var lng = (pos2.Longitude - pos1.Longitude).ToRadians();
var h1 = Math.Sin(lat / 2) * Math.Sin(lat / 2) +
Math.Cos(pos1.Latitude.ToRadians()) *
Math.Cos(pos2.Latitude.ToRadians()) *
Math.Sin(lng / 2) * Math.Sin(lng / 2);
var h2 = 2 * Math.Asin(Math.Min(1, Math.Sqrt(h1)));
return R * h2;
}
public enum DistanceUnit { Miles, Kilometers };
и
var Offices = GetMyOfficeList();
for(int i = 0; i< Offices.Count; i++)
{
Offices[i].Distance = HaversineDistance(
coord,
new LatLng(Offices[i].Lat, Offices[i].Lng),
DistanceUnit.Miles);
}
var closestOffice = Offices.OrderBy(x => x.Distance).Take(1).Single();
Масштабируемость важна, поскольку в моем сценарии может легко оказаться намного больше, чем 100 000 офисов, поэтому вариант со списком офисов в памяти маловероятен!