feat(hjm): 实现车辆位置坐标转换与违章查询功能
- 新增 GPS 坐标转换工具类,支持 WGS84、GCJ02 和 BD09 坐标系互转 - 在车辆定位页面集成坐标转换逻辑,确保地图显示准确性 -优化车辆列表分页加载功能,提升大数据量下的用户体验- 修改车辆热销组件属性类型定义,增强代码可维护性 - 调整违章记录列表页面,新增通过车辆编号查询功能 - 更新最佳销售车辆组件键值生成逻辑,提高组件稳定性 - 完善地图组件 markers 和 polygons 数据校验,防止无效坐标渲染 - 在车辆详情页增加违章查询跳转按钮,方便用户操作 - 移除违章记录删除功能,避免误删重要数据 -优化空状态提示文案,使用户理解更清晰
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import {useEffect} from "react";
|
||||
import {Image, Space} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
|
||||
const BestSellers = (props: any) => {
|
||||
interface BestSellersProps {
|
||||
data: HjmCar[];
|
||||
}
|
||||
|
||||
const BestSellers = (props: BestSellersProps) => {
|
||||
const reload = () => {
|
||||
|
||||
// 可以在这里添加重新加载逻辑
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -16,7 +21,7 @@ const BestSellers = (props: any) => {
|
||||
<div className={'flex flex-col justify-between items-center rounded-lg px-3'}>
|
||||
{props.data?.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className={'flex bg-white rounded-lg w-full p-3 mb-3'}
|
||||
<div key={item.id || index} className={'flex bg-white rounded-lg w-full p-3 mb-3'}
|
||||
onClick={() => Taro.navigateTo({url: '/hjm/query?id=' + item.code})}>
|
||||
{ item.image && (
|
||||
<Image src={JSON.parse(item.image)[0].url} mode={'scaleToFill'}
|
||||
@@ -40,8 +45,8 @@ const BestSellers = (props: any) => {
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{props.data.length === 0 && (
|
||||
<div className={'flex justify-center items-center'}>
|
||||
{(!props.data || props.data.length === 0) && (
|
||||
<div className={'flex justify-center items-center py-10'}>
|
||||
<div className={'text-gray-500 text-sm'}>暂无数据</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -50,4 +55,4 @@ const BestSellers = (props: any) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default BestSellers
|
||||
export default BestSellers
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {useEffect, useState, CSSProperties} from "react";
|
||||
import {Search} from '@nutui/icons-react-taro'
|
||||
import {Button, Input, InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
import {pageHjmCar} from "@/api/hjm/hjmCar";
|
||||
@@ -8,6 +8,14 @@ import './location.scss'
|
||||
import BestSellers from "./BestSellers";
|
||||
import {getUserInfo} from "@/api/layout";
|
||||
|
||||
const InfiniteUlStyle: CSSProperties = {
|
||||
height: '80vh',
|
||||
width: '100%',
|
||||
padding: '0',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章终极列表
|
||||
* @constructor
|
||||
@@ -15,14 +23,20 @@ import {getUserInfo} from "@/api/layout";
|
||||
const List = () => {
|
||||
const [keywords, setKeywords] = useState<string>('')
|
||||
const [list, setList] = useState<HjmCar[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const onKeywords = (keywords: string) => {
|
||||
setKeywords(keywords)
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const loadList = async (pageNum: number, isRefresh = false) => {
|
||||
if (loading) return;
|
||||
|
||||
setLoading(true)
|
||||
// 搜索条件
|
||||
const where = {status: 1, deleted: 0, keywords}
|
||||
const where = {status: 1, deleted: 0, keywords, page: pageNum, limit: 10}
|
||||
|
||||
// 读取用户信息
|
||||
const user = await getUserInfo();
|
||||
@@ -46,20 +60,53 @@ const List = () => {
|
||||
where.installerId = user.userId;
|
||||
}
|
||||
if(roleCode == 'user'){
|
||||
setLoading(false)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取车辆列表
|
||||
pageHjmCar(where).then(res => {
|
||||
setList(res?.list || [])
|
||||
})
|
||||
try {
|
||||
const res = await pageHjmCar(where);
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
if (isRefresh) {
|
||||
setList(res.list);
|
||||
} else {
|
||||
setList(prevList => [...prevList, ...res.list]);
|
||||
}
|
||||
setHasMore(res.list.length >= 10); // 如果返回的数据少于10条,说明没有更多了
|
||||
} else {
|
||||
if (isRefresh) {
|
||||
setList([]);
|
||||
}
|
||||
setHasMore(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取车辆列表失败:', error);
|
||||
if (isRefresh) {
|
||||
setList([]);
|
||||
}
|
||||
setHasMore(false);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
setPage(1);
|
||||
await loadList(1, true);
|
||||
}
|
||||
|
||||
const loadMore = async () => {
|
||||
if (!hasMore || loading) return;
|
||||
const nextPage = page + 1;
|
||||
setPage(nextPage);
|
||||
await loadList(nextPage);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'fixed z-20 top-5 left-0 w-full'}>
|
||||
@@ -90,12 +137,19 @@ const List = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<InfiniteLoading
|
||||
className={'w-full fixed left-0 top-20'}
|
||||
>
|
||||
<BestSellers data={list}/>
|
||||
</InfiniteLoading>
|
||||
<div style={InfiniteUlStyle} id="scroll">
|
||||
<InfiniteLoading
|
||||
target="scroll"
|
||||
className={'w-full fixed left-0 top-20'}
|
||||
hasMore={hasMore}
|
||||
onLoadMore={loadMore}
|
||||
loadingText="加载中..."
|
||||
loadMoreText="没有更多了"
|
||||
>
|
||||
<BestSellers data={list}/>
|
||||
</InfiniteLoading>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default List
|
||||
export default List
|
||||
@@ -5,6 +5,8 @@ import {Button, Input} from '@nutui/nutui-react-taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {getHjmCarByCode} from "@/api/hjm/hjmCar";
|
||||
import {HjmCar} from "@/api/hjm/hjmCar/model";
|
||||
// 导入整个GPS对象,因为gpsUtil.js导出的是一个对象而不是命名导出
|
||||
import GPS from '@/utils/gpsUtil.js';
|
||||
import './location.scss'
|
||||
|
||||
/**
|
||||
@@ -36,9 +38,17 @@ const Location = () => {
|
||||
// 转为多边形点数组
|
||||
const points = coordPairs.map(coord => {
|
||||
const [lat, lng] = coord.split(',');
|
||||
const latitude = parseFloat(lat);
|
||||
const longitude = parseFloat(lng);
|
||||
|
||||
// 确保解析后的坐标是有效数字
|
||||
if (isNaN(latitude) || isNaN(longitude)) {
|
||||
throw new Error(`无效坐标: ${lat}, ${lng}`);
|
||||
}
|
||||
|
||||
return {
|
||||
latitude: parseFloat(lat),
|
||||
longitude: parseFloat(lng)
|
||||
latitude: latitude,
|
||||
longitude: longitude
|
||||
}
|
||||
});
|
||||
|
||||
@@ -57,10 +67,41 @@ const Location = () => {
|
||||
getHjmCarByCode(keywords).then(data => {
|
||||
console.log('执行搜索', data)
|
||||
setItem(data)
|
||||
// setLatitude(data.latitude)
|
||||
// setLongitude(data.longitude)
|
||||
// wgs48坐标转gcj02坐标代
|
||||
|
||||
// 转换WGS84坐标到GCJ02坐标
|
||||
if (data.latitude && data.longitude &&
|
||||
!isNaN(data.latitude) &&
|
||||
!isNaN(data.longitude) &&
|
||||
isFinite(data.latitude) &&
|
||||
isFinite(data.longitude)) {
|
||||
console.log('原始坐标:', data.latitude, data.longitude)
|
||||
try {
|
||||
// 确保坐标是数字类型
|
||||
const lat = Number(data.latitude);
|
||||
const lng = Number(data.longitude);
|
||||
const transformed = GPS.gcj_encrypt(lat, lng);
|
||||
console.log(transformed, 'transformedtransformedtransformedtransformedtransformed')
|
||||
// 确保转换结果有效
|
||||
if (transformed && !isNaN(transformed.lat) && !isNaN(transformed.lng)) {
|
||||
setLatitude(transformed.lat);
|
||||
setLongitude(transformed.lng);
|
||||
console.log('转换结果:', transformed)
|
||||
} else {
|
||||
// 如果转换结果无效,使用原始坐标
|
||||
setLatitude(data.latitude);
|
||||
setLongitude(data.longitude);
|
||||
console.log('转换失败:', data.longitude)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('坐标转换错误:', error);
|
||||
// 如果转换出错,使用原始坐标
|
||||
setLatitude(data.latitude);
|
||||
setLongitude(data.longitude);
|
||||
}
|
||||
} else {
|
||||
// 如果坐标无效,使用原始坐标
|
||||
setLatitude(data.latitude);
|
||||
setLongitude(data.longitude);
|
||||
}
|
||||
|
||||
setKeywords(data.code)
|
||||
if (data.fence) {
|
||||
@@ -69,8 +110,44 @@ const Location = () => {
|
||||
|
||||
// 使用通用函数解析坐标字符串
|
||||
const {points} = parseCoordinateString(coordStr);
|
||||
|
||||
// 转换围栏坐标点
|
||||
if (points.length > 0) {
|
||||
const transformedPoints = points.map(point => {
|
||||
// 确保坐标有效
|
||||
if (point.latitude && point.longitude &&
|
||||
!isNaN(point.latitude) &&
|
||||
!isNaN(point.longitude) &&
|
||||
isFinite(point.latitude) &&
|
||||
isFinite(point.longitude)) {
|
||||
try {
|
||||
// 确保坐标是数字类型
|
||||
const lat = Number(point.latitude);
|
||||
const lng = Number(point.longitude);
|
||||
const transformed = GPS.gcj_encrypt(lat, lng);
|
||||
// 确保转换结果有效
|
||||
if (transformed && !isNaN(transformed.lat) && !isNaN(transformed.lng)) {
|
||||
return {
|
||||
latitude: transformed.lat,
|
||||
longitude: transformed.lng
|
||||
};
|
||||
} else {
|
||||
// 如果转换结果无效,返回原始坐标
|
||||
return point;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('围栏坐标转换错误:', error);
|
||||
// 如果转换出错,返回原始坐标
|
||||
return point;
|
||||
}
|
||||
}
|
||||
// 如果坐标无效,返回原始坐标
|
||||
return point;
|
||||
});
|
||||
setPoints(transformedPoints);
|
||||
}
|
||||
|
||||
console.log('解析结果 - 多边形点:', points);
|
||||
setPoints(points);
|
||||
setShowCircles(true)
|
||||
}
|
||||
})
|
||||
@@ -80,8 +157,38 @@ const Location = () => {
|
||||
if (code) {
|
||||
getHjmCarByCode(code).then(data => {
|
||||
setItem(data)
|
||||
setLatitude(data.latitude)
|
||||
setLongitude(data.longitude)
|
||||
// 转换WGS84坐标到GCJ02坐标
|
||||
if (data.latitude && data.longitude &&
|
||||
!isNaN(data.latitude) &&
|
||||
!isNaN(data.longitude) &&
|
||||
isFinite(data.latitude) &&
|
||||
isFinite(data.longitude)) {
|
||||
try {
|
||||
// 确保坐标是数字类型
|
||||
const lat = Number(data.latitude);
|
||||
const lng = Number(data.longitude);
|
||||
const transformed = GPS.gcj_encrypt(lat, lng);
|
||||
// 确保转换结果有效
|
||||
if (transformed && !isNaN(transformed.lat) && !isNaN(transformed.lng)) {
|
||||
setLatitude(transformed.lat);
|
||||
setLongitude(transformed.lng);
|
||||
} else {
|
||||
// 如果转换结果无效,使用原始坐标
|
||||
setLatitude(data.latitude);
|
||||
setLongitude(data.longitude);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('坐标转换错误:', error);
|
||||
// 如果转换出错,使用原始坐标
|
||||
setLatitude(data.latitude);
|
||||
setLongitude(data.longitude);
|
||||
}
|
||||
} else {
|
||||
// 如果坐标无效,使用原始坐标
|
||||
setLatitude(data.latitude);
|
||||
setLongitude(data.longitude);
|
||||
}
|
||||
|
||||
setKeywords(data.code)
|
||||
if (data.fence) {
|
||||
// 方法2:使用实际的 fence 数据(如果是字符串格式)
|
||||
@@ -89,8 +196,44 @@ const Location = () => {
|
||||
|
||||
// 使用通用函数解析坐标字符串
|
||||
const {points} = parseCoordinateString(coordStr);
|
||||
|
||||
// 转换围栏坐标点
|
||||
if (points.length > 0) {
|
||||
const transformedPoints = points.map(point => {
|
||||
// 确保坐标有效
|
||||
if (point.latitude && point.longitude &&
|
||||
!isNaN(point.latitude) &&
|
||||
!isNaN(point.longitude) &&
|
||||
isFinite(point.latitude) &&
|
||||
isFinite(point.longitude)) {
|
||||
try {
|
||||
// 确保坐标是数字类型
|
||||
const lat = Number(point.latitude);
|
||||
const lng = Number(point.longitude);
|
||||
const transformed = GPS.gcj_encrypt(lat, lng);
|
||||
// 确保转换结果有效
|
||||
if (transformed && !isNaN(transformed.lat) && !isNaN(transformed.lng)) {
|
||||
return {
|
||||
latitude: transformed.lat,
|
||||
longitude: transformed.lng
|
||||
};
|
||||
} else {
|
||||
// 如果转换结果无效,返回原始坐标
|
||||
return point;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('围栏坐标转换错误:', error);
|
||||
// 如果转换出错,返回原始坐标
|
||||
return point;
|
||||
}
|
||||
}
|
||||
// 如果坐标无效,返回原始坐标
|
||||
return point;
|
||||
});
|
||||
setPoints(transformedPoints);
|
||||
}
|
||||
|
||||
console.log('解析结果 - 多边形点:', points);
|
||||
setPoints(points);
|
||||
setShowCircles(true)
|
||||
}
|
||||
|
||||
@@ -194,31 +337,40 @@ const Location = () => {
|
||||
longitude={longitude}
|
||||
latitude={latitude}
|
||||
scale={scale}
|
||||
markers={[{
|
||||
id: 1,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
label: {
|
||||
content: `${item?.code}`,
|
||||
color: '#000000',
|
||||
fontSize: 12,
|
||||
borderRadius: 5,
|
||||
bgColor: '#FFFFFF',
|
||||
// @ts-ignore
|
||||
padding: '5px 5px',
|
||||
borderWidth: 1
|
||||
},
|
||||
}]}
|
||||
polygons={points.length > 0 ? [
|
||||
{
|
||||
points: points,
|
||||
color: '#ff0000',
|
||||
strokeWidth: 3
|
||||
}
|
||||
] : []}
|
||||
markers={
|
||||
latitude && longitude &&
|
||||
!isNaN(latitude) && !isNaN(longitude) &&
|
||||
isFinite(latitude) && isFinite(longitude)
|
||||
? [{
|
||||
id: 1,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
iconPath: ''
|
||||
}]
|
||||
: []
|
||||
}
|
||||
polygons={
|
||||
points.length > 0 &&
|
||||
points.every(point =>
|
||||
point.latitude && point.longitude &&
|
||||
!isNaN(point.latitude) && !isNaN(point.longitude) &&
|
||||
isFinite(point.latitude) && isFinite(point.longitude)
|
||||
)
|
||||
? [
|
||||
{
|
||||
points: points,
|
||||
color: '#ff0000',
|
||||
strokeWidth: 3
|
||||
}
|
||||
]
|
||||
: []
|
||||
}
|
||||
onTap={() => {
|
||||
console.log('map tap')
|
||||
}}
|
||||
onError={(e) => {
|
||||
console.error('地图组件错误:', e)
|
||||
}}
|
||||
style={{width: '100%', height: '100vh'}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -839,6 +839,15 @@ const Query = () => {
|
||||
}>
|
||||
位置信息
|
||||
</Button>
|
||||
<Button nativeType="submit" type="default" onClick={
|
||||
() => {
|
||||
Taro.navigateTo({
|
||||
url: `/hjm/violation/list?id=${FormData?.code}`
|
||||
})
|
||||
}
|
||||
}>
|
||||
违章查询
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ import {
|
||||
Space,
|
||||
Pagination
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {Search, Calendar, Truck, File, AddCircle} from '@nutui/icons-react-taro'
|
||||
import Taro, {useDidShow} from '@tarojs/taro'
|
||||
import {pageHjmViolation, removeHjmViolation} from "@/api/hjm/hjmViolation";
|
||||
import {pageHjmViolation} from "@/api/hjm/hjmViolation";
|
||||
import {HjmViolation} from "@/api/hjm/hjmViolation/model";
|
||||
|
||||
|
||||
@@ -18,6 +19,7 @@ import {HjmViolation} from "@/api/hjm/hjmViolation/model";
|
||||
* 报险记录列表页面
|
||||
*/
|
||||
const List: React.FC = () => {
|
||||
const {params} = useRouter();
|
||||
const [list, setList] = useState<HjmViolation[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [keywords, setKeywords] = useState<string>('')
|
||||
@@ -64,6 +66,9 @@ const List: React.FC = () => {
|
||||
where.organizationId = Taro.getStorageSync('OrganizationId');
|
||||
}
|
||||
}
|
||||
if(params.id){
|
||||
where.code = params.id;
|
||||
}
|
||||
const res = await pageHjmViolation(where)
|
||||
|
||||
setList(res?.list || [])
|
||||
@@ -183,7 +188,7 @@ const List: React.FC = () => {
|
||||
<Loading type="spinner">加载中...</Loading>
|
||||
</div>
|
||||
) : list.length === 0 ? (
|
||||
<Empty description="暂无报险记录">
|
||||
<Empty description="暂无违章记录">
|
||||
</Empty>
|
||||
) : (
|
||||
<div style={{padding: '0 16px'}}>
|
||||
@@ -292,22 +297,22 @@ const List: React.FC = () => {
|
||||
url: `/hjm/violation/add?id=${item.id}`
|
||||
})
|
||||
}}>修改</Button>
|
||||
<Button type="primary" onClick={(event: any) => {
|
||||
event.stopPropagation()
|
||||
removeHjmViolation(item.id).then(() => {
|
||||
Taro.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
})
|
||||
// 删除成功后重新加载列表
|
||||
reload(false)
|
||||
}).catch((error) => {
|
||||
Taro.showToast({
|
||||
title: error.message || '删除失败',
|
||||
icon: 'error'
|
||||
})
|
||||
})
|
||||
}}>删除</Button>
|
||||
{/*<Button type="primary" onClick={(event: any) => {*/}
|
||||
{/* event.stopPropagation()*/}
|
||||
{/* removeHjmViolation(item.id).then(() => {*/}
|
||||
{/* Taro.showToast({*/}
|
||||
{/* title: '删除成功',*/}
|
||||
{/* icon: 'success'*/}
|
||||
{/* })*/}
|
||||
{/* // 删除成功后重新加载列表*/}
|
||||
{/* reload(false)*/}
|
||||
{/* }).catch((error) => {*/}
|
||||
{/* Taro.showToast({*/}
|
||||
{/* title: error.message || '删除失败',*/}
|
||||
{/* icon: 'error'*/}
|
||||
{/* })*/}
|
||||
{/* })*/}
|
||||
{/*}}>删除</Button>*/}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
|
||||
203
src/utils/gpsUtil.js
Normal file
203
src/utils/gpsUtil.js
Normal file
@@ -0,0 +1,203 @@
|
||||
var GPS = {
|
||||
PI: 3.14159265358979324,
|
||||
x_pi: 3.14159265358979324 * 3000.0 / 180.0,
|
||||
delta: function(lat, lng) {
|
||||
// Krasovsky 1940
|
||||
//
|
||||
// a = 6378245.0, 1/f = 298.3
|
||||
// b = a * (1 - f)
|
||||
// ee = (a^2 - b^2) / a^2;
|
||||
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
|
||||
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
|
||||
var dLat = this.transformLat(lng - 105.0, lat - 35.0);
|
||||
var dLon = this.transformLon(lng - 105.0, lat - 35.0);
|
||||
var radLat = lat / 180.0 * this.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) * this.PI);
|
||||
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
|
||||
return {
|
||||
'lat': dLat,
|
||||
'lng': dLon
|
||||
};
|
||||
},
|
||||
|
||||
//WGS-84 to GCJ-02 ,传入的数据要是Number
|
||||
gcj_encrypt: function(wgsLat, wgsLon) {
|
||||
if (this.outOfChina(wgsLat, wgsLon))
|
||||
return {
|
||||
'lat': wgsLat,
|
||||
'lng': wgsLon
|
||||
};
|
||||
|
||||
var d = this.delta(wgsLat, wgsLon);
|
||||
return {
|
||||
'lat': Number( (wgsLat + d.lat).toFixed(6)),
|
||||
'lng': Number( (wgsLon + d.lng).toFixed(6))
|
||||
};
|
||||
},
|
||||
|
||||
//WGS-84 to GCJ-02
|
||||
gcj_encrypt222: function(wgsLat, wgsLon) {
|
||||
if (this.outOfChina(wgsLat, wgsLon))
|
||||
return wgsLat + "," + wgsLon;
|
||||
|
||||
var d = this.delta(wgsLat, wgsLon);
|
||||
|
||||
var lat = (wgsLat + d.lat).toFixed(6);
|
||||
var lng = (wgsLon + d.lng).toFixed(6);
|
||||
return lat+ "," + lng
|
||||
|
||||
},
|
||||
|
||||
|
||||
//GCJ-02 to WGS-84
|
||||
gcj_decrypt: function(gcjLat, gcjLon) {
|
||||
if (this.outOfChina(gcjLat, gcjLon))
|
||||
return {
|
||||
'lat': Number((gcjLat).toFixed(6)),
|
||||
'lng': Number((gcjLon).toFixed(6))
|
||||
};
|
||||
|
||||
var d = this.delta(gcjLat, gcjLon);
|
||||
return {
|
||||
'lat': Number( (gcjLat - d.lat).toFixed(6)),
|
||||
'lng': Number( (gcjLon - d.lng).toFixed(6))
|
||||
};
|
||||
},
|
||||
//GCJ-02 to WGS-84 exactly
|
||||
gcj_decrypt_exact: function(gcjLat, gcjLon) {
|
||||
var initDelta = 0.01;
|
||||
var threshold = 0.000000001;
|
||||
var dLat = initDelta,
|
||||
dLon = initDelta;
|
||||
var mLat = gcjLat - dLat,
|
||||
mLon = gcjLon - dLon;
|
||||
var pLat = gcjLat + dLat,
|
||||
pLon = gcjLon + dLon;
|
||||
var wgsLat, wgsLon, i = 0;
|
||||
while (1) {
|
||||
wgsLat = (mLat + pLat) / 2;
|
||||
wgsLon = (mLon + pLon) / 2;
|
||||
var tmp = this.gcj_encrypt(wgsLat, wgsLon)
|
||||
dLat = tmp.lat - gcjLat;
|
||||
dLon = tmp.lng - gcjLon;
|
||||
if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold))
|
||||
break;
|
||||
|
||||
if (dLat > 0) pLat = wgsLat;
|
||||
else mLat = wgsLat;
|
||||
if (dLon > 0) pLon = wgsLon;
|
||||
else mLon = wgsLon;
|
||||
|
||||
if (++i > 10000) break;
|
||||
}
|
||||
//console.log(i);
|
||||
return {
|
||||
'lat': (wgsLat).toFixed(6),
|
||||
'lng': (wgsLon).toFixed(6)
|
||||
};
|
||||
},
|
||||
//GCJ-02 to BD-09
|
||||
bd_encrypt: function(gcjLat, gcjLon) {
|
||||
var x = gcjLon,
|
||||
y = gcjLat;
|
||||
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
|
||||
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
|
||||
bdLon = z * Math.cos(theta) + 0.0065;
|
||||
bdLat = z * Math.sin(theta) + 0.006;
|
||||
return {
|
||||
'lat': (bdLat).toFixed(6),
|
||||
'lng': (bdLon).toFixed(6)
|
||||
};
|
||||
},
|
||||
//BD-09 to GCJ-02
|
||||
bd_decrypt: function(bdLat, bdLon) {
|
||||
var x = bdLon - 0.0065,
|
||||
y = bdLat - 0.006;
|
||||
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
|
||||
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
|
||||
var gcjLon = z * Math.cos(theta);
|
||||
var gcjLat = z * Math.sin(theta);
|
||||
return {
|
||||
'lat': (gcjLat).toFixed(6),
|
||||
'lng': (gcjLon).toFixed(6)
|
||||
};
|
||||
},
|
||||
//WGS-84 to Web mercator
|
||||
//mercatorLat -> y mercatorLon -> x
|
||||
mercator_encrypt: function(wgsLat, wgsLon) {
|
||||
var x = wgsLon * 20037508.34 / 180.;
|
||||
var y = Math.log(Math.tan((90. + wgsLat) * this.PI / 360.)) / (this.PI / 180.);
|
||||
y = y * 20037508.34 / 180.;
|
||||
return {
|
||||
'lat': (y).toFixed(6),
|
||||
'lng': (x).toFixed(6)
|
||||
};
|
||||
/*
|
||||
if ((Math.abs(wgsLon) > 180 || Math.abs(wgsLat) > 90))
|
||||
return null;
|
||||
var x = 6378137.0 * wgsLon * 0.017453292519943295;
|
||||
var a = wgsLat * 0.017453292519943295;
|
||||
var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
|
||||
return {'lat' : y, 'lng' : x};
|
||||
//*/
|
||||
},
|
||||
// Web mercator to WGS-84
|
||||
// mercatorLat -> y mercatorLon -> x
|
||||
mercator_decrypt: function(mercatorLat, mercatorLon) {
|
||||
var x = mercatorLon / 20037508.34 * 180.;
|
||||
var y = mercatorLat / 20037508.34 * 180.;
|
||||
y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180.)) - this.PI / 2);
|
||||
return {
|
||||
'lat': (y).toFixed(6),
|
||||
'lng': (x).toFixed(6)
|
||||
};
|
||||
/*
|
||||
if (Math.abs(mercatorLon) < 180 && Math.abs(mercatorLat) < 90)
|
||||
return null;
|
||||
if ((Math.abs(mercatorLon) > 20037508.3427892) || (Math.abs(mercatorLat) > 20037508.3427892))
|
||||
return null;
|
||||
var a = mercatorLon / 6378137.0 * 57.295779513082323;
|
||||
var x = a - (Math.floor(((a + 180.0) / 360.0)) * 360.0);
|
||||
var y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * mercatorLat) / 6378137.0)))) * 57.295779513082323;
|
||||
return {'lat' : y, 'lng' : x};
|
||||
//*/
|
||||
},
|
||||
// two point's distance
|
||||
distance: function(latA, lonA, latB, lonB) {
|
||||
var earthR = 6371000.;
|
||||
var x = Math.cos(latA * this.PI / 180.) * Math.cos(latB * this.PI / 180.) * Math.cos((lonA - lonB) * this.PI / 180);
|
||||
var y = Math.sin(latA * this.PI / 180.) * Math.sin(latB * this.PI / 180.);
|
||||
var s = x + y;
|
||||
if (s > 1) s = 1;
|
||||
if (s < -1) s = -1;
|
||||
var alpha = Math.acos(s);
|
||||
var distance = alpha * earthR;
|
||||
return distance;
|
||||
},
|
||||
outOfChina: function(lat, lng) {
|
||||
if (lng < 72.004 || lng > 137.8347)
|
||||
return true;
|
||||
if (lat < 0.8293 || lat > 55.8271)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
transformLat: function(x, y) {
|
||||
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 * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
|
||||
ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
},
|
||||
transformLon: function(x, y) {
|
||||
var 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 * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
|
||||
ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = GPS;
|
||||
Reference in New Issue
Block a user