首先需要知道:坐标系大致如下
当设备模块获取到北斗数据后,上传到服务端后需要做数据过滤,因为北斗的精度大致是在6米范围内,且经常有数据误报,隔三岔五就有跨洲的经纬度出现,就比较离谱,当然,也有不是很离谱的误差,定位偏差了十几米,不管怎么样,偏差的数据都会影响我们对信息的处理,所以需要有一个北斗数据清洗过滤的过程,提供大致思路如下:
1、首先根据数据提供层的数据格式约定淘汰无效数据。比如笔者就与底层约定:底层提供的经纬度数据为小数点后保留6位的字符串。然后我会校验这个约定,如果不满足则丢弃数据。为什么要校验这个?有时候底层数据读写异常就会导致数据格式明显异常,那么当后端处理后映射到地图上呈现的效果就是直接飘过大西洋了…笔者就遇到内陆飘到非洲的定位…这样就能淘汰格式异常的数据。
2、做数据运动逻辑校验。就比如笔者的设备的极限运动速度为0.5m/s,每2秒上报一次速度,那么在考虑到北斗本身5-6米定位偏差的情况下,如果一次上报的定位数据与上一条定位数据距离相差了10多米,那多半是有问题的,可以考虑舍弃数据。那么问题又来了,我们如何所谓上一条数据的有效性呢?设计思路如下:
一开始设备处于无定位状态,在设备提供北斗数据后就连续获取数据,将第一条数据先存储下来,等到第二条数据上来就判断与第一条数据的距离是否在合理偏差范围内,比如距离为10米以内。如果在合理范围内则可判断第二条数据为正确有效的数据,则后面第三条数据就可以与这条有效数据继续做校验从而判断其有效性。还要注意一点,就是有效数据的时间有效性,可以用redis的过期时间来做有效数据过期,比如已经判断一条数据有效,如果之后40秒上报的数据都无法建立有效数据,那么这条有效数据也会判定为无效数据。
总的来说就是:根据设备极限运动性能通过判断两条相邻的数据建立有效数据,在有效数据时间不过期的情况下,不断获取并建立有效数据,循环与之后的数据做比较。
// BdToGd 北斗坐标转换成高德坐标
func BdToGd(lonPlace, latPlace string)(finalLon, finalLat string) {
//北斗坐标转 DB09坐标
//⾸先,要强调⼀点,其实我们
//北⽃定位的坐标系值并不是简单的100倍关系,⽽是需要做⼀次度分秒的转换的。则我们获取的北⽃坐标值,
//北纬2429.53531,东经11810.78036,需要做如下计算:
//24+(29.53531/60)≈ 24.49225517
//118+(10.78036/60)≈118.17967267
lon := lonPlace
lat := latPlace
lonDm, _ := decimal.NewFromString(lon)
latDm, _ := decimal.NewFromString(lat)
hundred := decimal.NewFromInt(100)
lonDiv := lonDm.Div(hundred)
latDiv := latDm.Div(hundred)
lonIntDm := decimal.NewFromInt(lonDiv.IntPart())
latIntDm := decimal.NewFromInt(latDiv.IntPart())
sixty := decimal.NewFromInt(60)
lonAfter := lonDiv.Sub(lonIntDm).Mul(hundred).Div(sixty)
latAfter := latDiv.Sub(latIntDm).Mul(hundred).Div(sixty)
lonFinal := lonIntDm.Add(lonAfter).Round(20).InexactFloat64()
latFinal := latIntDm.Add(latAfter).Round(20).InexactFloat64()
//fmt.Sprintf("输入数据:"+lonPlace+","+latPlace+"--->"+"服务端处理后:%.20f,%.20f",lonFinal,latFinal)
resLon, resLat :=GCJEncrypt(lonFinal,latFinal)
finalLon = strconv.FormatFloat(resLon, 'f', -1, 64)
finalLat = strconv.FormatFloat(resLat, 'f', -1, 64)
fmt.Printf("输入数据:%s,%s-->中转处理:%.20f,%.20f-->高德结果:%.20f,%.20f",lonPlace,latPlace,lonFinal,latFinal, resLon, resLat)
return
}
// GCJEncrypt ..
func GCJEncrypt(wgsLon,wgsLat float64) (float64, float64) {
if wgsLon < 72.004 || wgsLon > 137.8347 || wgsLat < 0.8293 || wgsLat > 55.8271 {
return wgsLat, wgsLon
}
PI := 3.14159265358979324
a := 6378245.0 // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
ee := 0.00669342162296594323 // ee: 椭球的偏心率。
var x = wgsLon - 105.0
var y = wgsLat - 35.0
var ret = -100.0 + 2.0*x + 3.0*y + 0.2*y*y + 0.1*x*y + 0.2*math.Sqrt(math.Abs(x))
ret += (20.0*math.Sin(6.0*x*PI) + 20.0*math.Sin(2.0*x*PI)) * 2.0 / 3.0
ret += (20.0*math.Sin(y*PI) + 40.0*math.Sin(y/3.0*PI)) * 2.0 / 3.0
ret += (160.0*math.Sin(y/12.0*PI) + 320*math.Sin(y*PI/30.0)) * 2.0 / 3.0
var dLat = ret
x = wgsLon - 105.0
y = wgsLat - 35.0
ret = 300.0 + x + 2.0*y + 0.1*x*x + 0.1*x*y + 0.1*math.Sqrt(math.Abs(x))
ret += (20.0*math.Sin(6.0*x*PI) + 20.0*math.Sin(2.0*x*PI)) * 2.0 / 3.0
ret += (20.0*math.Sin(x*PI) + 40.0*math.Sin(x/3.0*PI)) * 2.0 / 3.0
ret += (150.0*math.Sin(x/12.0*PI) + 300.0*math.Sin(x/30.0*PI)) * 2.0 / 3.0
var dLon = ret
var radLat = wgsLat / 180.0 * PI
var magic = math.Sin(radLat)
magic = 1 - ee*magic*magic
var sqrtMagic = math.Sqrt(magic)
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI)
dLon = (dLon * 180.0) / (a / sqrtMagic * math.Cos(radLat) * PI)
return wgsLon + dLon, wgsLat + dLat
}
// JudgeBdData 判断北斗数据有效行和过滤无效数据
func JudgeBdData(uuid string, lonNow, latNow string) bool {
//BdToGd(lonNow,latNow)
lonNowFloat, _ := strconv.ParseFloat(lonNow, 64)
latNowFloat, _ := strconv.ParseFloat(latNow, 64)
//判断是否存在正确的北斗定位坐标,不存在就查询上一条数据,进行10m内判断,如果上一条数据也不存在,就把这条数据存为上一条数据,然后return
if global.GVA_REDIS.Exists("right-data:"+uuid).Val() == 0 {
//没有正确的数据
if global.GVA_REDIS.Exists("before-data:"+uuid).Val() == 0 {
//不存在上一条数据,则把当前数据插入before-data,有效存储6秒
global.GVA_REDIS.Set("before-data:"+uuid, lonNow+","+latNow, time.Second*6)
return false
} else {
//获取before-data,判断10m,判断这条数据是否正确
data := global.GVA_REDIS.Get("before-data:" + uuid).Val()
before_lonlat := strings.SplitN(data, ",", -1)
lonFloat, _ := strconv.ParseFloat(before_lonlat[0], 64)
latFloat, _ := strconv.ParseFloat(before_lonlat[1], 64)
global.GVA_REDIS.GeoAdd("useful-gps:"+uuid, &redis.GeoLocation{
Name: "before",
Longitude: lonFloat,
Latitude: latFloat,
Dist: 0,
GeoHash: 0,
})
count := global.GVA_REDIS.GeoRadius("useful-gps:"+uuid, lonNowFloat, latNowFloat, &redis.GeoRadiusQuery{
Radius: 10,
Unit: "m",
WithCoord: false,
WithDist: false,
WithGeoHash: false,
Count: 1,
Sort: "",
Store: "",
StoreDist: "",
}).Val()
if len(count) == 0 {
//两条数据在10m外,此条数据不能判断为正确数据,把此条数据存入before-data
global.GVA_REDIS.Del("useful-gps:" + uuid)
global.GVA_REDIS.Set("before-data:"+uuid, lonNow+","+latNow, time.Second*6)
return false
} else {
//两条数据在10m内,此条数据为有效数据
global.GVA_REDIS.Set("right-data:"+uuid, lonNow+","+latNow, time.Second*40)
return true
}
}
} else {
//存在正确定位数据
right_data := global.GVA_REDIS.Get("right-data:" + uuid).Val()
rights := strings.SplitN(right_data, ",", -1)
lonRightFloat, _ := strconv.ParseFloat(rights[0], 64)
latRightFloat, _ := strconv.ParseFloat(rights[1], 64)
global.GVA_REDIS.GeoAdd("useful-gps:"+uuid, &redis.GeoLocation{
Name: "before",
Longitude: lonRightFloat,
Latitude: latRightFloat,
Dist: 0,
GeoHash: 0,
})
count_right := global.GVA_REDIS.GeoRadius("useful-gps:"+uuid, lonNowFloat, latNowFloat, &redis.GeoRadiusQuery{
Radius: 20,
Unit: "m",
WithCoord: false,
WithDist: false,
WithGeoHash: false,
Count: 1,
Sort: "",
Store: "",
StoreDist: "",
}).Val()
if len(count_right) > 0 {
global.GVA_REDIS.Set("right-data:"+uuid, lonNow+","+latNow, time.Second*40)
global.GVA_REDIS.Del("useful-gps:" + uuid)
return true
} else {
global.GVA_REDIS.Del("useful-gps:" + uuid)
return false
}
}
}