Я пытался использовать приведенный выше код, и ответы были слишком много, когда расстояние между точками было в диапазоне 20-30 миль, и я в порядке с ошибкой в несколько миль. Поговорили с моим приятелем по картированию, и мы вместо этого придумали этот. Код Python, но вы можете перевести его довольно легко. Чтобы избежать постоянного преобразования в радианы, я переделал свою базу данных, преобразовав точки широты / долготы из градусов в радианы. Приятно то, что большая часть математики выполняется в основном один раз.
ra = 3963.1906 # radius @ equator in miles, change to km if you want distance in km
rb = 3949.90275 # radius @ poles in miles, change to km if you want distance in km
ra2 = ra * ra
rb2 = rb * rb
phi = self.lat
big_ol_constant = (math.pow(ra2*math.cos(phi), 2) + pow(rb2*math.sin(phi), 2))/ (pow(ra*math.cos(phi), 2) + pow(rb*math.sin(phi), 2))
sqlWhere = "%(distance)g > sqrt((power(lat - %(lat)g,2) + power(lng-%(lng)g,2)) * %(big_ol_constant)g)" % {
'big_ol_constant': big_ol_constant, 'lat': self.lat, 'lng': self.lng, 'distance': distance}
# This is the Django portion of it, where the ORM kicks in. sqlWhere is what you would put after the WHERE part of your SQL Query.
qs = ZipData.objects.extra(where=[sqlWhere]);
Кажется очень точным, когда расстояние между ними невелико, и в пределах 10 миль или около того, когда расстояние увеличивается до 200 миль (конечно, к тому времени у вас возникнут проблемы с "по прямой линии" против "дорог с твердым покрытием") .
Вот модель ZipData, о которой я упоминал выше.
class ZipData(models.Model):
zipcode = ZipCodeField(null=False, blank=False, verbose_name="ZipCode", primary_key=True)
city = models.CharField(max_length=32, null=False, blank=False)
state = models.CharField(max_length=2)
lat = models.FloatField(null=False, blank=False)
lng = models.FloatField(null=False, blank=False)
Еще одно замечание: вы можете получить МНОГО геоданных, связанных с почтовыми кодами, по адресу GeoNames.org , и у них даже есть некоторые API веб-сервисов, которые вы также можете использовать.