219 lines
7.3 KiB
Python
219 lines
7.3 KiB
Python
#!/bin/python
|
||
#coding: utf-8
|
||
# +-------------------------------------------------------------------
|
||
# | system: django-vue-lyadmin
|
||
# +-------------------------------------------------------------------
|
||
# | Author: lybbn
|
||
# +-------------------------------------------------------------------
|
||
# | QQ: 1042594286
|
||
# +-------------------------------------------------------------------
|
||
|
||
# ------------------------------
|
||
# 经纬度工具类封装
|
||
# ------------------------------
|
||
|
||
from urllib import parse
|
||
import hashlib
|
||
import json
|
||
import math
|
||
from urllib.request import urlopen
|
||
import requests
|
||
from math import radians, cos, sin, asin, sqrt, degrees, atan2
|
||
from django.db.models.expressions import RawSQL
|
||
|
||
# ================================================= #
|
||
# ******************** 根据经度纬度匹配最近的点 ******************** #
|
||
# ================================================= #
|
||
def validate_point(lat,lon):
|
||
assert -90 <= lat <= 90, "bad latitude"
|
||
assert -180 <= lon <= 180, "bad longitude"
|
||
|
||
# original formula from http://www.movable-type.co.uk/scripts/latlong.html
|
||
def distance_haversine(lat1, lon1, lat2, lon2):
|
||
"""
|
||
Calculate the great circle distance between two points
|
||
on the earth (specified in decimal degrees)
|
||
Haversine
|
||
formula:
|
||
a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
|
||
_ ____
|
||
c = 2 ⋅ atan2( √a, √(1−a) )
|
||
d = R ⋅ c
|
||
|
||
where φ is latitude, λ is longitude, R is earth’s radius (mean radius = 6,371km);
|
||
note that angles need to be in radians to pass to trig functions!
|
||
"""
|
||
|
||
validate_point(lat1, lon1)
|
||
validate_point(lat2, lon2)
|
||
|
||
|
||
R = 6371 # km - earths's radius
|
||
|
||
# convert decimal degrees to radians
|
||
lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2])
|
||
|
||
# haversine formula
|
||
dlon = lon2 - lon1
|
||
dlat = lat2 - lat1
|
||
|
||
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
|
||
c = 2 * asin(sqrt(a)) # 2 * atan2(sqrt(a), sqrt(1-a))
|
||
d = R * c
|
||
return d
|
||
|
||
|
||
#根据经纬度匹配最近的商家,按distance距离排序
|
||
def get_locations_nearby_queryset(queryset,latitude, longitude, max_distance=None):
|
||
"""
|
||
Return objects sorted by distance to specified coordinates
|
||
which distance is less than max_distance given in kilometers
|
||
queryset 要查询的商家的查询集
|
||
latitude 纬度
|
||
longitude 经度
|
||
max_distance 自定义的最大查询距离:自带单位km
|
||
"""
|
||
# Great circle distance formula
|
||
gcd_formula = "6371 * acos(least(greatest(\
|
||
cos(radians(%s)) * cos(radians(latitude)) \
|
||
* cos(radians(longitude) - radians(%s)) + \
|
||
sin(radians(%s)) * sin(radians(latitude)) \
|
||
, -1), 1))"
|
||
distance_raw_sql = RawSQL(
|
||
gcd_formula,
|
||
(latitude, longitude, latitude)
|
||
)
|
||
qs = queryset.annotate(distance=distance_raw_sql).order_by('distance')
|
||
if max_distance is not None:
|
||
qs = qs.filter(distance__lt=max_distance)
|
||
return qs
|
||
|
||
# ================================================= #
|
||
# ******************** 百度地图详细地址解析成经纬度 ******************** #
|
||
# ================================================= #
|
||
|
||
ak = 'LYGmcld2pgHXuocf4bqsyHABEfT9lf1B'
|
||
sk = 'gAaECvBKFjeuLzlwVKNp5r80MRA7zMMe'
|
||
|
||
# 百度地图根据详细地址获取经纬度
|
||
def getbaidulnglat(address):
|
||
|
||
# 以get请求为例http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=你的ak
|
||
queryStr = '/geocoding/v3/?address=%s&output=json&ak=%s' % (address,ak)
|
||
|
||
|
||
# 对queryStr进行转码,safe内的保留字符不转换
|
||
encodedStr = parse.quote(queryStr, safe="/:=&?#+!$,;'@()*[]")
|
||
|
||
# 在最后直接追加上yoursk
|
||
rawStr = encodedStr + sk
|
||
|
||
# 计算sn
|
||
sn = (hashlib.md5(parse.quote_plus(rawStr).encode("utf8")).hexdigest())
|
||
|
||
# 由于URL里面含有中文,所以需要用parse.quote进行处理,然后返回最终可调用的url
|
||
uri = parse.quote("http://api.map.baidu.com" +queryStr +"&sn=" +sn, safe="/:=&?#+!$,;'@()*[]")
|
||
|
||
# print(uri)
|
||
try:
|
||
req = urlopen(uri)
|
||
res = req.read().decode()
|
||
temp = json.loads(res)
|
||
# print(temp)
|
||
# 纬度
|
||
lat = temp['result']['location']['lat']
|
||
# 经度
|
||
lng = temp['result']['location']['lng']
|
||
# 地址查找失败
|
||
if math.isclose(lat, 39.910925, rel_tol=1e-5):
|
||
lat = None
|
||
if math.isclose(lng, 116.413384, rel_tol=1e-5):
|
||
lng = None
|
||
except Exception as e:
|
||
# print(e)
|
||
lng = None
|
||
lat = None
|
||
|
||
return {'lng':lng,'lat':lat}
|
||
|
||
|
||
def computeMD5(message):
|
||
m = hashlib.md5()
|
||
m.update(message.encode(encoding='utf-8'))
|
||
return m.hexdigest()
|
||
# ================================================================= #
|
||
# ******************** 腾讯地图详细地址解析成经纬度 ******************** #
|
||
# =================================================================#
|
||
#限制 10,000 次/日
|
||
#限制 并发 5 次/秒
|
||
key = "M4NBZ-STTK5-OARIA-Q7T4R-YO5OQ-MTB7O"
|
||
secretkey='uCUAQVJKvx5OgvHuOYK5uFzI0CdisBvm'
|
||
def gettecentlnglat(address):
|
||
# url = 'https://apis.map.qq.com/ws/geocoder/v1/?address='+address+'&key='+key
|
||
queryStr1 = '/ws/geocoder/v1/'
|
||
queryStr2 = 'address='+address+'&key='+key
|
||
# 计算sig
|
||
sig = computeMD5(queryStr1+'?'+queryStr2+secretkey)
|
||
url = 'https://apis.map.qq.com'+queryStr1+'?'+queryStr2+'&sig='+sig
|
||
try:
|
||
response = requests.get(url)
|
||
res = response.json()
|
||
# print(res)
|
||
# 纬度
|
||
lat = res['result']['location']['lat']
|
||
# 经度
|
||
lng = res['result']['location']['lng']
|
||
except Exception as e:
|
||
lng = None
|
||
lat = None
|
||
|
||
return {'lng': lng, 'lat': lat}
|
||
|
||
# {
|
||
# "status": 0,
|
||
# "message": "query ok",
|
||
# "result": {
|
||
# "title": "海淀西大街74号",
|
||
# "location": {
|
||
# "lng": 116.307015,
|
||
# "lat": 39.982915
|
||
# },
|
||
# "ad_info": {
|
||
# "adcode": "110108"
|
||
# },
|
||
# "address_components": {
|
||
# "province": "北京市",
|
||
# "city": "北京市",
|
||
# "district": "海淀区",
|
||
# "street": "海淀西大街",
|
||
# "street_number": "74"
|
||
# },
|
||
# "similarity": 0.8,
|
||
# "deviation": 1000,
|
||
# "reliability": 7,
|
||
# "level": 9
|
||
# }
|
||
# }
|
||
|
||
# ========================================================================= #
|
||
# ******************** 腾讯地图逆地址解析-经纬度解析成地址信息 ******************** #
|
||
# =========================================================================#
|
||
def gettecentaddress(location):
|
||
"""
|
||
location=lat<纬度>,lng<经度>
|
||
例如:location= 39.984154,116.307490
|
||
"""
|
||
# url = 'https://apis.map.qq.com/ws/geocoder/v1/?address='+address+'&key='+key
|
||
queryStr1 = '/ws/geocoder/v1/'
|
||
queryStr2 = 'key='+key+'&location='+location
|
||
# 计算sig
|
||
sig = computeMD5(queryStr1+'?'+queryStr2+secretkey)
|
||
url = 'https://apis.map.qq.com'+queryStr1+'?'+queryStr2+'&sig='+sig
|
||
try:
|
||
response = requests.get(url)
|
||
res = response.json()
|
||
# print(res)
|
||
except Exception as e:
|
||
res=None
|
||
|
||
return res |