上記のコードを使用してみましたが、ポイント間の距離が 20 ~ 30 マイルの範囲にある場合、答えが大きくずれていました。数マイルの誤差があっても問題ありません。私のマッピング仲間と話し、代わりにこれを思いつきました. コードは python ですが、かなり簡単に翻訳できます。ラジアンへの一定の変換を避けるために、データベースを再編集し、緯度/経度のポイントを度からラジアンに変換しました。これの良いところは、計算の大部分がほとんど 1 回で済むことです。
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]);
離れた距離が小さい場合は非常に正確であるように思われ、距離が 200 マイルになると 10 マイル程度以内になります (もちろん、それまでには、「カラスが飛ぶように」対「舗装された道路」で問題が発生します)。
これが、上で言及したモデル 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で郵便番号に関連する多数の地理データを取得でき、同様に使用できる Web サービス API もいくつか用意されています。