逆地理编码-离线版-part2

本文主要提供工具类代码:

AdminUtils.py

# -*- coding: utf-8 -*-


import re

NATIONS = "阿昌族,鄂温克族,傈僳族,水族,白族,高山族,珞巴族,塔吉克族,保安族,仡佬族,满族,塔塔尔族,布朗族,哈尼族,毛南族,土家族,布依族,哈萨克族,门巴族,土族,朝鲜族,汉族,蒙古族,佤族,达斡尔族,赫哲族,苗族,维吾尔族,傣族,回族,仫佬族,乌孜别克族,德昂族,基诺族,纳西族,锡伯族,东乡族,京族,怒族,瑶族,侗族,景颇族,普米族,彝族,独龙族,柯尔克孜族,羌族,裕固族,俄罗斯族,拉祜族,撒拉族,藏族,鄂伦春族,黎族,畲族,壮族".split(
    ",")

p1 = """(.+)(?:省|市)"""
p2 = """(.+)自治区"""
p3 = """(.+)特别行政区"""

c0 = """^(.{2})$"""  # 2 长度为2的 "东区" "南区"
c1 = """(.+)(?:自治州|自治县)$"""  # 30 自治州  琼中黎族苗族自治县
c2 = """(.+)[市|盟|州]$"""  # 304 地级市, 盟; + 1恩施州
c3 = """(.+)地区$"""  # 8 地区
c4 = """(.+)(?:群岛|填海区)$"""  # 2 东沙群岛
c5 = """(.+[^地郊城堂])区$"""  # 20 港澳 不含"东区" "南区"2个字的
c6 = """(.+)(?:城区|郊县)$"""  # 6 九龙城区,上海城区,天津城区,北京城区,重庆城区,重庆郊县
c7 = """(.+[^郊])县$"""  # 12 台湾的xx县

d0 = """^(.{2})$"""  # 2 长度为2的 "随县"
d1 = """(.+)[市]$"""  # 304 城区 “赤水市”
d2 = """(.+)自治县$"""  # 30 自治县
d3 = """(.+)自治州直辖$"""  # 30 自治州直辖 "海西蒙古族藏族自治州直辖"
d4 = """(.+)[区|县]$"""  # 8 区县
d5 = """(.+)(?:乡|镇|街道)$"""  # 8 乡镇|街道

s0 = """^(.{2})$"""
s1 = """(.+)(?:特别行政管理区|街道办事处|旅游经济特区|民族乡|地区街道)$"""
s2 = """(.+)(?:镇|乡|村|街道|苏木|老街|管理区|区公所|苏木|办事处|社区|经济特区|行政管理区)$"""


def replaceNations(ncity: str):
    for e in NATIONS:
        ncity = ncity.replace(e, '')
    return ncity

    # return zip([ncity] ++ NATIONS).reduce((x, y) => x.replaceAll(y, "").replaceAll(if(y.length > 2) y.replaceAll("族", "") else "", ""))


def shortProvince(province: str):
    # (.+)特别行政区
    # (.+省|.+自治区)(.+市)
    res = re.match(p1, province, flags=0)
    if res:
        return res.group()

    res = re.match(p2, province, flags=0)
    if res:
        return res.group()

    res = re.match(p3, province, flags=0)
    if res:
        return res.group()
    # case p1(x) => x
    # case p2(x) => if(x== "内蒙古") x else replaceNations(x)
    # case p3(x) => x
    # case _ => province

    return province


def shortCityImp(city: str):
    """
    :param city:
    :return: (city,-1)
    """
    # 总数 383
    if re.match(c0, city, flags=0):
        return re.match(c0, city, flags=0).group(), 0
    elif re.match(c1, city, flags=0):
        return re.match(c1, city, flags=0).group(), 1
    elif re.match(c2, city, flags=0):
        return re.match(c2, city, flags=0).group(), 2
    elif re.match(c3, city, flags=0):
        return re.match(c3, city, flags=0).group(), 3
    elif re.match(c4, city, flags=0):
        return re.match(c4, city, flags=0).group(), 4
    elif re.match(c5, city, flags=0):
        return re.match(c5, city, flags=0).group(), 5
    elif re.match(c6, city, flags=0):
        return re.match(c6, city, flags=0).group(), 6
    elif re.match(c7, city, flags=0):
        return re.match(c7, city, flags=0).group(), 7

    # case c0(x) => (x, 0)
    # case c1(x) => (replaceNations(x), 2)
    # case c2(x) => if(x == "襄樊") ("襄阳",1) else (x, 1)
    # case c3(x) => (x,3)
    # case c4(x) => (x,4)
    # case c5(x) => (x,5)
    # case c6(x) => (x,6)
    # case c7(x) => (x,7)
    # case _ => (city, -1)
    return city, -1


def shortDistrictImp(district: str):
    """
    
    :param district: 
    :return: (String, Int) 
    """
    # // 总数 2963 56个内蒙八旗和新疆兵团没有处理
    if re.match(d0, district, flags=0):
        return re.match(d0, district, flags=0).group()
    elif re.match(d1, district, flags=0):
        return re.match(d1, district, flags=0).group()
    elif re.match(d2, district, flags=0):
        return replaceNations(re.match(d2, district, flags=0).group()),2
    elif re.match(d3, district, flags=0):
        return replaceNations(re.match(d3, district, flags=0).group()),3
    elif re.match(d4, district, flags=0):
        return re.match(d4, district, flags=0).group()
    elif re.match(d5, district, flags=0):
        return re.match(d5, district, flags=0).group()
    # match {
    #  case d0(x) => (x, 0)
    #  case d1(x) => (x, 1)
    #  case d2(x) => (replaceNations(x), 2)
    #  case d3(x) => (replaceNations(x), 3)
    #  case d4(x) => (x,4)
    #  case d5(x) => (x,5)
    #  case _ => (district, -1)


def replaceNationsNotEmpty(name: str):
    for e in NATIONS:
        name_ = name.replace(e, '').replace(e.replace('族', ''))
        if len(name_) >= 0:
            name = name_
    return name


def shortStreetImp(street: str):
    """
    :param street:
    :return:
    """
    # // 总数 42387
    # // 柘城县邵园乡人民政府, 保安镇, 鹅湖镇人民政府, 东风地区
    if re.match(s0, street, flags=0):
        return re.match(s0, street, flags=0).group(), 0
    elif re.match(s1, street, flags=0):
        return replaceNationsNotEmpty(re.match(s1, street, flags=0).group()), 1
    elif re.match(s2, street, flags=0):
        return replaceNationsNotEmpty(re.match(s2, street, flags=0).group()), 2
    # street match {
    #   case s0(x) => (x, 0)
    #   case s1(x) => (replaceNationsNotEmpty(x), 1)
    #   case s2(x) => (replaceNationsNotEmpty(x), 2)
    #   case _ => (street, -1)
    # }
    return street, -1


if __name__ == '__main__':
    shortProvince("安徽省宿州市埇桥区")
    shortProvince("天津市")
    shortProvince("内蒙古自治区")
    shortProvince("香港特别行政区")

 GeoUtils.py

import math
from s2sphere import LatLng, Angle

from geo_obj import Location, CoordinateSystem

x_PI = math.pi * 3000.0 / 180.0
EE = 0.00669342162296594323
A = 6378245.0  # BJZ54坐标系地球长半轴, m
EQUATOR_C = 20037508.3427892  # 赤道周长, m
EARTH_RADIUS = 6378137.0  # WGS84, CGCS2000坐标系地球长半轴, m
EARTH_POLAR_RADIUS = 6356725.0  # 极半径, m

SQRT2 = 1.414213562


def outOfChina(lng, lat):
    return lng < 72.004 or lng > 137.8347 or lat < 0.8293 or lat > 55.8271


def transformLat(lng: float, lat: float):
    ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * math.sqrt(abs(lng))
    ret += (20.0 * math.sin(6.0 * lng * math.pi) + 20.0 * math.sin(2.0 * lng * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lat * math.pi) + 40.0 * math.sin(lat / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (160.0 * math.sin(lat / 12.0 * math.pi) + 320 * math.sin(lat * math.pi / 30.0)) * 2.0 / 3.0
    return ret


def transformLng(lng: float, lat: float):
    ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * math.sqrt(abs(lng))
    ret += (20.0 * math.sin(6.0 * lng * math.pi) + 20.0 * math.sin(2.0 * lng * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lng * math.pi) + 40.0 * math.sin(lng / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (150.0 * math.sin(lng / 12.0 * math.pi) + 300.0 * math.sin(lng / 30.0 * math.pi)) * 2.0 / 3.0
    return ret


def rad(d: float):
    return d * math.pi / 180.0


def get_earth_dist(locA: LatLng, locB: LatLng, radius=6367000.0):
    """
    :param locA:
    :param locB:
    :param radius:
    :return:
    """
    lat1 = locA.lat().radians;
    lat2 = locB.lat().radians;
    lng1 = locA.lng().radians;
    lng2 = locB.lng().radians;
    dlat = math.sin(0.5 * (lat2 - lat1));
    dlng = math.sin(0.5 * (lng2 - lng1));
    x = dlat * dlat + dlng * dlng * math.cos(lat1) * math.cos(lat2);
    dis = (2.0 * math.atan2(math.sqrt(x), math.sqrt(max(0.0, 1.0 - x))))
    dist = dis * radius
    return dist


def get_earth_distance(locA: LatLng, locB: LatLng):
    """
    :param locA: .degrees or radians
    :param locB:
    :return:
    """
    try:
        # print(f'locA={locA},locB={locB}')
        lngA = float(str(locA).split(',')[1])
        latA = float(str(locA).split(',')[0].split(" ")[1])

        lngB = float(str(locB).split(',')[1])
        latB = float(str(locB).split(',')[0].split(" ")[1])

        f = rad((latA + latB) / 2)
        g = rad((latA - latB) / 2)
        l = rad((lngA - lngB) / 2)
        if g == 0 and l == 0:
            return 0
        sg = math.sin(g)
        sl = math.sin(l)
        sf = math.sin(f)
        s = .0
        c = .0
        w = .0
        r = .0
        d = .0
        h1 = .0
        h2 = .0
        dis = .0
        a = EARTH_RADIUS
        fl = 1 / 298.257
        sg = sg * sg
        sl = sl * sl
        sf = sf * sf
        s = sg * (1 - sl) + (1 - sf) * sl
        c = (1 - sg) * (1 - sl) + sf * sl
        w = math.atan(math.sqrt(s / c))
        r = math.sqrt(s * c) / w
        d = 2 * w * a
        h1 = (3 * r - 1) / 2 / c
        h2 = (3 * r + 1) / 2 / s
        dis = d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg))
    except:
        print('get_earth_distance failed')
        return -1
    return float(f"{dis:.2f}")


def distance(locA: Location, locB: Location):
    lngA = locA.lng
    latA = locA.lat
    lngB = locB.lng
    latB = locB.lat
    f = rad((latA + latB) / 2)
    g = rad((latA - latB) / 2)
    l = rad((lngA - lngB) / 2)
    if (g == 0 and l == 0):
        return 0
    sg = math.sin(g)
    sl = math.sin(l)
    sf = math.sin(f)
    s = .0
    c = .0
    w = .0
    r = .0
    d = .0
    h1 = .0
    h2 = .0
    dis = .0
    a = EARTH_RADIUS
    fl = 1 / 298.257
    sg = sg * sg
    sl = sl * sl
    sf = sf * sf
    s = sg * (1 - sl) + (1 - sf) * sl
    c = (1 - sg) * (1 - sl) + sf * sl
    w = math.atan(math.sqrt(s / c))
    r = math.sqrt(s * c) / w
    d = 2 * w * a
    h1 = (3 * r - 1) / 2 / c
    h2 = (3 * r + 1) / 2 / s
    dis = d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg))

    return float(f"{dis:.2f}")


def wgs84ToGCj02(lng: float, lat: float):
    """

    :param lng:
    :param lat:
    :return:
    """
    mglat = .0
    mglng = .0

    if outOfChina(lng, lat):
        mglat = lat
        mglng = lng

    else:
        dLat = transformLat(lng - 105.0, lat - 35.0)
        dLon = transformLng(lng - 105.0, lat - 35.0)
        radLat = lat / 180.0 * math.pi
        magic = math.sin(radLat)
        magic = 1 - EE * magic * magic
        sqrtMagic = math.sqrt(magic)
        dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * math.pi)
        dLon = (dLon * 180.0) / (A / sqrtMagic * math.cos(radLat) * math.pi)
        mglat = lat + dLat
        mglng = lng + dLon

    return mglng, mglat


def toGCJ02(lng: float, lat: float, coordType: CoordinateSystem):
    """
    判断坐标系转换
    :param lng:
    :param lat:
    :param coordType:
    :return:
    """
    if coordType.value == CoordinateSystem.WGS84.value:
        d = wgs84ToGCj02(lng, lat)
        return d
    if coordType.value == CoordinateSystem.BD09.value:
        d = bd09ToGCJ02(lng, lat)
        return d
    return lng, lat


def gcj02ToWgs84(lng: float, lat: float):
    if outOfChina(lng, lat):
        return lng, lat
    dlat = transformLat(lng - 105.0, lat - 35.0)
    dlng = transformLng(lng - 105.0, lat - 35.0)
    radlat = lat / 180.0 * math.pi
    magic = math.sin(radlat)
    magic = 1 - EE * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * math.pi)
    dlng = (dlng * 180.0) / (A / sqrtmagic * math.cos(radlat) * math.pi)
    mglat = lat + dlat
    mglng = lng + dlng
    return lng * 2 - mglng, lat * 2 - mglat


def toWGS84(lng: float, lat: float, coordType: CoordinateSystem):
    """
    判断坐标系转换
    :type lat: object
    :param lng:
    :param lat:
    :param coordType:
    :return:
    """
    # print("CoordinateSystem.GCJ02 compare", coordType == CoordinateSystem.GCJ02,dir(coordType),dir(CoordinateSystem.GCJ02))
    if coordType.value == CoordinateSystem.GCJ02.value:
        return gcj02ToWgs84(lng, lat)
    elif coordType.value == CoordinateSystem.BD09.value:
        d02 = bd09ToGCJ02(lng, lat)
        return gcj02ToWgs84(d02[0], d02[0])
    else:
        return lng, lat


def bd09ToGCJ02(lng: float, lat: float):
    if outOfChina(lng, lat):
        return lng, lat
    x = lng - 0.0065
    y = lat - 0.006
    z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_PI)
    theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_PI)
    gg_lng = z * math.cos(theta)
    gg_lat = z * math.sin(theta)
    return gg_lng, gg_lat

 LineUtils.py

# // 计算两点之间的距离

import math


def lineDis(x1: float, y1: float, x2: float, y2: float):
    return math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))


def pointToLineDis(x1: float , y1: float , x2: float , y2: float , x0: float , y0: float ): 

    a = lineDis(x1, y1, x2, y2) #  线段的长度
    b = lineDis(x1, y1, x0, y0) #  点到起点的距离
    c = lineDis(x2, y2, x0, y0) #  点到终点的距离
    # 点在端点上
    if c <= 0.000001 or b <= 0.000001:
        return 0
    
    # 直线距离过短
    if a <= 0.000001:
        return b
    
    #  点在起点左侧,距离等于点到起点距离
    if c * c >= a * a + b * b:
        return b
    
    # 点在终点右侧,距离等于点到终点距离
    if b * b >= a * a + c * c:
        return c
    # 点在起点和终点中间,为垂线距离
    k = (y2 - y1) / (x2 - x1)
    z = y1 - k * x1
    p = (a + b + c) / 2
    #  半周长
    s = math.sqrt(p * (p - a) * (p - b) * (p - c))  # 海伦公式求面积
    return 2 * s / a  # 回点到线的距离(利用三角形面积公式求高)

S2Utils.py

import math
from s2sphere import Cap
from s2sphere import RegionCoverer
from s2sphere import *

kEarthCircumferenceMeters = 1000 * 40075.017


def earthMeters2Radians(meters: float)  :
    return (2 * math.pi) * (meters / 40075017)


# // 预算,提升速度
def earthMeters2Radians_(radius):
    rad = earthMeters2Radians(radius * 1000)
    return radius * 1000, rad * rad * 2


capHeightMap_ = map(lambda radius:earthMeters2Radians_(radius),[2, 4, 8, 16, 32, 64, 128, 256])
capHeightMap = {}
for e1,e2 in capHeightMap_:
    capHeightMap[e1] = e2
print(capHeightMap)


def getLevel(inputs: int):
    """
    :param inputs:
    :return:
    """
    n = 0
    input = inputs
    # print(input, inputs)
    while input % 2 == 0:
        input = input / 2
        n += 1
    return 30 - n / 2


def getCellId(s2LatLng: LatLng, radius: int, desLevel: int):

    capHeight = capHeightMap.get(radius) if capHeightMap.get(radius) else 0

    cap = Cap.from_axis_height(s2LatLng.to_point(), capHeight)

    coverer = RegionCoverer()
    coverer.min_level = desLevel
    coverer.max_level = desLevel
    """
    >>> a = A()
    >>> getattr(a, 'bar')          # 获取属性 bar 值
    >>> setattr(a, 'bar', 5)       # 设置属性 bar 值
    """
    #  圆形内的cell会自动做聚合,手动拆分

    res = []
    cellIds = coverer.get_covering(cap)

    for s2CellId in cellIds:
        cellLevel = getLevel(s2CellId.id())
        if (cellLevel == desLevel):
            res.append(s2CellId.id())
        else:
            res.extend([cellid.id() for cellid in childrenCellId(s2CellId, cellLevel, desLevel)])
    return res


def childrenCellId(s2CellId: CellId, curLevel: int, desLevel: int):
    list = []
    if (curLevel < desLevel) :
      inter= (s2CellId.childEnd.id - s2CellId.childBegin.id) / 4
      for i in range(0,5):
          id = s2CellId.childBegin.id + inter * i
          cellId = CellId(id)
          list.append( childrenCellId(cellId, curLevel + 1, desLevel))
    else:
        list.append(s2CellId)
    return list


if __name__ == '__main__':
    getLevel(100)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mtj66

看心情

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值