1. 程式人生 > >如何計算用戶在某個車站上下車,地圖匹配方法

如何計算用戶在某個車站上下車,地圖匹配方法

list hash ror err traffic == %s url 使用

具體需求
基於電信的位置融合數據,分析用戶是否通過火車出行,以及火車出行的相關信息,如乘車車次、上車站、下車站等。

數據描述
1、用戶定位數據
手機號碼
業務開始時間
城市編碼
經緯度
基站標識
網格號
業務類型
事件類型
數據源
天分區
2、火車停靠數據
車次
車站序號
車站名稱
行駛時間
到站時間
行駛裏程
經緯度

Python代碼如下:
輸入經緯度 和編碼級別計算對應的geohash編碼

from math import log10

__base32 = ‘0123456789bcdefghjkmnpqrstuvwxyz‘
__decodemap = { }
for i in range(len(__base32)):
    __decodemap[__base32[i]] = i
del i

def decode_exactly(geohash):
    lat_interval, lon_interval = (-90.0, 90.0), (-180.0, 180.0)
    lat_err, lon_err = 90.0, 180.0
    is_even = True
    for c in geohash:
        cd = __decodemap[c]
        for mask in [16, 8, 4, 2, 1]:
            if is_even: # adds longitude info
                lon_err /= 2
                if cd & mask:
                    lon_interval = ((lon_interval[0]+lon_interval[1])/2, lon_interval[1])
                else:
                    lon_interval = (lon_interval[0], (lon_interval[0]+lon_interval[1])/2)
            else:      # adds latitude info
                lat_err /= 2
                if cd & mask:
                    lat_interval = ((lat_interval[0]+lat_interval[1])/2, lat_interval[1])
                else:
                    lat_interval = (lat_interval[0], (lat_interval[0]+lat_interval[1])/2)
            is_even = not is_even
    lat = (lat_interval[0] + lat_interval[1]) / 2
    lon = (lon_interval[0] + lon_interval[1]) / 2
    return lat, lon, lat_err, lon_err

def decode(geohash):

    lat, lon, lat_err, lon_err = decode_exactly(geohash)
    # Format to the number of decimals that are known
    lats = "%.*f" % (max(1, int(round(-log10(lat_err)))) - 1, lat)
    lons = "%.*f" % (max(1, int(round(-log10(lon_err)))) - 1, lon)
    if ‘.‘ in lats: lats = lats.rstrip(‘0‘)
    if ‘.‘ in lons: lons = lons.rstrip(‘0‘)
    return lats, lons

def encode(latitude, longitude, precision=12):
    """
    Encode a position given in float arguments latitude, longitude to
    a geohash which will have the character count precision.
    """
    lat_interval, lon_interval = (-90.0, 90.0), (-180.0, 180.0)
    geohash = []
    bits = [ 16, 8, 4, 2, 1 ]
    bit = 0
    ch = 0
    even = True
    while len(geohash) < precision:
        if even:
            mid = (lon_interval[0] + lon_interval[1]) / 2
            if longitude > mid:
                ch |= bits[bit]
                lon_interval = (mid, lon_interval[1])
            else:
                lon_interval = (lon_interval[0], mid)
        else:
            mid = (lat_interval[0] + lat_interval[1]) / 2
            if latitude > mid:
                ch |= bits[bit]
                lat_interval = (mid, lat_interval[1])
            else:
                lat_interval = (lat_interval[0], mid)
        even = not even
        if bit < 4:
            bit += 1
        else:
            geohash += __base32[ch]
            bit = 0
            ch = 0
    return ‘‘.join(geohash)

上面的decode是解碼算法 encode是編碼算法

Google S2地圖索引算法
編碼過程和解碼過程如下:

hilbert_map = {
    ‘a‘: {(0, 0): (0, ‘d‘), (0, 1): (1, ‘a‘), (1, 0): (3, ‘b‘), (1, 1): (2, ‘a‘)},
    ‘b‘: {(0, 0): (2, ‘b‘), (0, 1): (1, ‘b‘), (1, 0): (3, ‘a‘), (1, 1): (0, ‘c‘)},
    ‘c‘: {(0, 0): (2, ‘c‘), (0, 1): (3, ‘d‘), (1, 0): (1, ‘c‘), (1, 1): (0, ‘b‘)},
    ‘d‘: {(0, 0): (0, ‘a‘), (0, 1): (3, ‘c‘), (1, 0): (1, ‘d‘), (1, 1): (2, ‘d‘)},
}

un_hilbert_map = {
    ‘a‘: { 0: (0, 0,‘d‘), 1: (0, 1,‘a‘), 3: (1, 0,‘b‘),  2: (1, 1,‘a‘)},
    ‘b‘: { 2: (0, 0,‘b‘), 1: (0, 1,‘b‘), 3: (1, 0,‘a‘),  0: (1, 1,‘c‘)},
    ‘c‘: { 2: (0, 0,‘c‘), 3: (0, 1,‘d‘), 1: (1, 0,‘c‘),  0: (1, 1,‘b‘)},
    ‘d‘: { 0: (0, 0,‘a‘), 3: (0, 1,‘c‘), 1: (1, 0,‘d‘),  2: (1, 1,‘d‘)}
}
#編碼def point_to_hilbert(lng,lat, order=16):
    print (‘hilbert‘)
    lng_range = [-180.0, 180.0]
    lat_range = [-90.0, 90.0]

    current_square = ‘a‘
    position = 0

    for i in range(order - 1, -1, -1):
        position <<= 2

        lng_mid = (lng_range[0]+lng_range[1])/2
        lat_mid = (lat_range[0]+lat_range[1])/2

        if lng >= lng_mid :
            quad_x = 1
            lng_range[0] = lng_mid
        else:
            quad_x = 0
            lng_range[1] = lng_mid

        if lat >= lat_mid :
            quad_y = 1
            lat_range[0] = lat_mid
        else:
            quad_y = 0
            lat_range[1] = lat_mid
        quad_position,current_square = hilbert_map[current_square][(quad_x, quad_y)]
        position |= quad_position
    return position
#解碼def hilbert_to_point( d , order=16):
    print (‘hilbert‘)
    lng_range = [-180.0, 180.0]
    lat_range = [-90.0, 90.0]

    current_square = ‘a‘
    lng=lat=lng_mid=lat_mid=0
    for i in range(order - 1, -1, -1):

        lng_mid = ( lng_range[0] + lng_range[1] ) / 2
        lat_mid = ( lat_range[0] + lat_range[1] ) / 2

        mask = 3 << (2*i)
        quad_position = (d & mask) >> (2*i)

        quad_x, quad_y, current_square= un_hilbert_map[current_square][quad_position]

        if quad_x:
            lng_range[0] = lng_mid
        else:
            lng_range[1] = lng_mid

        if quad_y:
            lat_range[0] = lat_mid
        else:
            lat_range[1] = lat_mid

    lat = lat_range[0]
    lng = lng_range[0]

    return lng,lat
if __name__ == ‘__main__‘:
    d = point_to_hilbert(-50.555443,77.776655,36)
    print (d)
    lng,lat = hilbert_to_point(d,36)
    print (lng,lat)

獲取百度路徑規劃算法結果的代碼如下:
對應的百度地圖API
參考代碼中的鏈接

import logging
import sys
import traceback
from time import sleep
import json
import requests
from bs4 import BeautifulSoup
import pymysql
import os.path
import time

def getroadinfo(par):
    linearray = par.split(‘#&;‘)
    startlon = linearray[0]
    startlat = linearray[1]
    endlon = linearray[2]
    endlat = linearray[3]
    strcount = linearray[4]
    #http: // api.map.baidu.com / direction / v1?mode = driving & origin = 上地五街 & destination = 北京大學 & origin_region = 北京 & destination_region = 北京 & output = json & ak = 您的ak
    origin = str(startlat) + "," + str(startlon)
    dest = str(endlat) + "," + str(endlon)
    url = "http://api.map.baidu.com/direction/v1";
    Param = "mode=driving&origin=" + origin + "&destination=" + dest + "&origin_region=武漢&destination_region=武漢&output=json&ak=" + "";
    strurl = url + "?" + Param;
    #請求百度接口返回數據
    try:
        web = requests.get(strurl, timeout=30)
        data = json.loads(web.text)
        if ‘result‘ in data:
            if ‘routes‘ in data[‘result‘]:
                routes = data[‘result‘][‘routes‘]
                listtext = []
                writestr=""
                for route in routes:
                    if ‘steps‘ in route:
                        steps = route[‘steps‘]
                        for step in steps:
                            # area = 0
                            # if ‘area‘ in step:
                            #     area = step[‘area‘]
                            # direction = 0
                            # if ‘direction‘ in step:
                            #     direction = step[‘direction‘]
                            # distance = 0
                            # if ‘distance‘ in step:
                            #     distance = step[‘distance‘]
                            # duration = 0
                            # if ‘duration‘ in step:
                            #     duration = step[‘duration‘]
                            # instructions = ""
                            # if ‘instructions‘ in step:
                            #     instructions = step[‘instructions‘]
                            path = ""
                            if ‘path‘ in step:
                                path = step[‘path‘]
                            # turn = 0
                            # if ‘turn‘ in step:
                            #     turn = step[‘turn‘]
                            # type = 0
                            # if ‘type‘ in step:
                            #     type = step[‘type‘]
                            # stepOriginInstruction = ""
                            # if ‘stepOriginInstruction‘ in step:
                            #     stepOriginInstruction = step[‘stepOriginInstruction‘]
                            # stepDestinationInstruction = ""
                            # if ‘stepDestinationInstruction‘ in step:
                            #     stepDestinationInstruction = step[‘stepDestinationInstruction‘]
                            # traffic_condition = 0
                            # if ‘traffic_condition‘ in step:
                            #     traffic_condition = step[‘traffic_condition‘]
                            # currenttime = time.strftime(‘%Y-%m-%d %H:%M:%S‘,time.localtime(time.time()))
                            # writestr = origin + "#&;" + dest + "#&;" + str(area) +"#&;"+ str(direction) +"#&;"+ str(distance) +"#&;"+ str(duration) +"#&;"+ instructions +"#&;"+ path +"#&;"+ str(turn) +"#&;"+ str(type) +"#&;"+ stepOriginInstruction +"#&;"+ stepDestinationInstruction +"#&;"+ str(traffic_condition) +"#&;"+ currenttime + "\n"
                            writestr = writestr + path + ";"
                print(time.strftime(‘%Y-%m-%d %H:%M:%S‘, time.localtime(time.time())))
                print(writestr)
                writestr = writestr + "\n"
                listtext.append(writestr)
                yushu = int(strcount)%20
                text = "E:/roadinfo/roadinfo"+str(yushu)+".txt"
                file_object = open(text, ‘a‘)
                lock(file_object, LOCK_EX)
                file_object.writelines(listtext)
                unlock(file_object)
                file_object.close()
    except:
        logging.error(‘解析線路信息失敗‘)
        traceback.print_exc()

計算步驟
為了計算所有用戶可能乘坐的火車班次和上下車車站具體的火車站,具體計算過程如下:
方案1

1、通過火車班次數據中的第三列和第七列

計算出所有的火車運行時速

2、對所有的用戶定位經緯度進行geohash編碼或者S2編碼存成另外一個列
3、參考geohash編碼級別對應的定位精度表如下:
Geohash 字符串長度 緯度 經度 緯度誤差 經度誤差 km誤差
1 2 3 ±23 ±23 ±2500
2 5 5 ±2.8 ±5.6 ±630
3 7 8 ±0.70 ±0.70 ±78
4 10 10 ±0.087 ±0.18 ±20
5 12 13 ±0.022 ±0.022 ±2.4
6 15 15 ±0.0027 ±0.0055 ±0.61
7 17 18 ±0.00068 ±0.00068 ±0.076
8 20 20 ±0.000085 ±0.00017 ±0.019

如果用戶在某一個小時的時間間隔內 geohash的前三位有變動,我們可以認為該用戶在高速移動中
4、篩選出所有geohash在一個小時內前三位有變動的點的用戶數據,
5、對所有的火車站進行geohash第五級編碼
6、如果某個用戶被過濾後的數據的geohash編碼和某個車站的geohash編碼的前五位相同,則判斷該用戶的定位時間是否在該geohash的車站的到站時間前後半個小時之內,如果在半個小時之內,則判斷該用戶前後的車站是否在某一個火車形成序列中,如果是則輸出。

方案2
1、因為考慮到用戶的數據並不是以一定的頻率勻速上傳,中間有間斷的可能,所以只能采用路網匹配的算法,即判斷某個用戶某個時間的數據點是否在道路網上:
2、參考百度地圖API,獲取百度地圖的鐵路網路徑規劃信息,具體爬蟲程序參考我上面的獲取路徑規劃的程序。以火車時刻表的每個班車每個時間段的起點和終點(車站)作為輸入獲取道路網數據。
3、對所有鐵路道路網點進行geohash第五級編碼 也就是定位精度為2.4公裏
4、對所有用戶數據的經緯度進行geohash第五級編碼
5、對用戶時間進行格式轉化為小時和分鐘兩個列
6、對道路網數據進行時間補充操作例如:

A B C

如上圖A點經過B點到達C點 總共時間一個小時,A到B路程為四分之一,已知A,C時間 則B的時間點為A時間加上一刻鐘。
之後將所有道路網時間轉化為 小時和分鐘兩個列
7、對用戶數據表和道路網表的時間數據列和 geohash列添加索引
註意是前綴索引,可以另外加兩個列是geohash的前綴,通過前綴匹配會加速。

8、使用鐵路網數據的geohash列對用戶定位數據進行過濾,去除所有不在火車道路網附近的用戶數據
9、對每一組用戶數據,和道路網的數據進行 geohash列的join
10、判斷時間是否和對應的時間點吻合,吻合即輸出

如何計算用戶在某個車站上下車,地圖匹配方法