From 2e54b6e85b63a40202fa03ce1b0dc5896fa74752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=AE=E5=BE=AE=E4=B8=80=E7=AC=91?= <709648985@qq.com> Date: Thu, 11 Sep 2025 09:49:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=9B=86=E6=88=90=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E5=9D=90=E6=A0=87=E7=B3=BB=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/coordinate-transform.ts | 211 ++++++++++++++++++++++++++++++ src/utils/index.ts | 12 ++ 2 files changed, 223 insertions(+) create mode 100644 src/utils/coordinate-transform.ts diff --git a/src/utils/coordinate-transform.ts b/src/utils/coordinate-transform.ts new file mode 100644 index 0000000..8a85009 --- /dev/null +++ b/src/utils/coordinate-transform.ts @@ -0,0 +1,211 @@ +/** + * 坐标系转换工具 + * WGS84(地球坐标系)与 GCJ02(火星坐标系)相互转换 + */ + +// 地标转国测常量 +const X_PI = (3.14159265358979324 * 3000.0) / 180.0; +const PI = 3.1415926535897932384626; +const A = 6378245.0; // 卫星椭球坐标投影到平面地图坐标系的投影因子 +const EE = 0.00669342162296594323; // 椭球的偏心率 + +/** + * 坐标点类型定义 + */ +export interface CoordinatePoint { + lng: number; // 经度 + lat: number; // 纬度 +} + +/** + * 判断是否在国内,在中国国内的经纬度才需要做偏移 + * @param lng 经度 + * @param lat 纬度 + * @returns 是否在国外 + */ +function outOfChina(lng: number, lat: number): boolean { + return ( + lng < 72.004 || + lng > 137.8347 || + lat < 0.8293 || + lat > 55.8271 + ); +} + +/** + * 转化经度 + * @param lng 经度 + * @param lat 纬度 + * @returns 转换后的经度偏移量 + */ +function transformLng(lng: number, lat: number): number { + let ret = + 300.0 + + lng + + 2.0 * lat + + 0.1 * lng * lng + + 0.1 * lng * lat + + 0.1 * Math.sqrt(Math.abs(lng)); + + ret += + ((20.0 * Math.sin(6.0 * lng * PI) + + 20.0 * Math.sin(2.0 * lng * PI)) * + 2.0) / + 3.0; + + ret += + ((20.0 * Math.sin(lng * PI) + + 40.0 * Math.sin((lng / 3.0) * PI)) * + 2.0) / + 3.0; + + ret += + ((150.0 * Math.sin((lng / 12.0) * PI) + + 300.0 * Math.sin((lng / 30.0) * PI)) * + 2.0) / + 3.0; + + return ret; +} + +/** + * 转化纬度 + * @param lng 经度 + * @param lat 纬度 + * @returns 转换后的纬度偏移量 + */ +function transformLat(lng: number, lat: number): number { + let ret = + -100.0 + + 2.0 * lng + + 3.0 * lat + + 0.2 * lat * lat + + 0.1 * lng * lat + + 0.2 * Math.sqrt(Math.abs(lng)); + + ret += + ((20.0 * Math.sin(6.0 * lng * PI) + + 20.0 * Math.sin(2.0 * lng * PI)) * + 2.0) / + 3.0; + + ret += + ((20.0 * Math.sin(lat * PI) + + 40.0 * Math.sin((lat / 3.0) * PI)) * + 2.0) / + 3.0; + + ret += + ((160.0 * Math.sin((lat / 12.0) * PI) + + 320 * Math.sin((lat * PI) / 30.0)) * + 2.0) / + 3.0; + + return ret; +} + +/** + * WGS84坐标系转GCJ02坐标系(地球坐标系转火星坐标系) + * @param lng 经度 + * @param lat 纬度 + * @returns 转换后的坐标 [经度, 纬度] + */ +export function wgs84ToGcj02(lng: number, lat: number): [number, number] { + if (outOfChina(lng, lat)) { + return [lng, lat]; + } + + const dlat = transformLat(lng - 105.0, lat - 35.0); + const dlng = transformLng(lng - 105.0, lat - 35.0); + const radlat = (lat / 180.0) * PI; + let magic = Math.sin(radlat); + magic = 1 - EE * magic * magic; + const sqrtmagic = Math.sqrt(magic); + + const transformedLat = + (dlat * 180.0) / + (((A * (1 - EE)) / (magic * sqrtmagic)) * PI); + const transformedLng = + (dlng * 180.0) / ((A / sqrtmagic) * Math.cos(radlat) * PI); + + const mglat = lat + transformedLat; + const mglng = lng + transformedLng; + + return [mglng, mglat]; +} + +/** + * GCJ02坐标系转WGS84坐标系(火星坐标系转地球坐标系) + * @param lng 经度 + * @param lat 纬度 + * @returns 转换后的坐标 [经度, 纬度] + */ +export function gcj02ToWgs84(lng: number, lat: number): [number, number] { + if (outOfChina(lng, lat)) { + return [lng, lat]; + } + + const dlat = transformLat(lng - 105.0, lat - 35.0); + const dlng = transformLng(lng - 105.0, lat - 35.0); + const radlat = (lat / 180.0) * PI; + let magic = Math.sin(radlat); + magic = 1 - EE * magic * magic; + const sqrtmagic = Math.sqrt(magic); + + const transformedLat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * PI); + const transformedLng = (dlng * 180.0) / (A / sqrtmagic * Math.cos(radlat) * PI); + + const mglat = lat + transformedLat; + const mglng = lng + transformedLng; + + return [lng * 2 - mglng, lat * 2 - mglat]; +} + +/** + * 对象形式的坐标转换 - WGS84转GCJ02 + * @param point 坐标点对象 {lng: 经度, lat: 纬度} + * @returns 转换后的坐标点对象 + */ +export function wgs84ToGcj02Point(point: CoordinatePoint): CoordinatePoint { + const [lng, lat] = wgs84ToGcj02(point.lng, point.lat); + return { lng, lat }; +} + +/** + * 对象形式的坐标转换 - GCJ02转WGS84 + * @param point 坐标点对象 {lng: 经度, lat: 纬度} + * @returns 转换后的坐标点对象 + */ +export function gcj02ToWgs84Point(point: CoordinatePoint): CoordinatePoint { + const [lng, lat] = gcj02ToWgs84(point.lng, point.lat); + return { lng, lat }; +} + +/** + * 批量转换坐标点 - WGS84转GCJ02 + * @param points 坐标点数组 + * @returns 转换后的坐标点数组 + */ +export function batchWgs84ToGcj02(points: CoordinatePoint[]): CoordinatePoint[] { + return points.map(point => wgs84ToGcj02Point(point)); +} + +/** + * 批量转换坐标点 - GCJ02转WGS84 + * @param points 坐标点数组 + * @returns 转换后的坐标点数组 + */ +export function batchGcj02ToWgs84(points: CoordinatePoint[]): CoordinatePoint[] { + return points.map(point => gcj02ToWgs84Point(point)); +} + +// 默认导出 +export default { + wgs84ToGcj02, + gcj02ToWgs84, + wgs84ToGcj02Point, + gcj02ToWgs84Point, + batchWgs84ToGcj02, + batchGcj02ToWgs84, + outOfChina +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index 2b0aad5..0a5fea0 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -316,3 +316,15 @@ export const removeClass = (ele: HTMLElement, cls: string) => { export const isExternal = (path: string) => { return /^(https?:|http?:|mailto:|tel:)/.test(path); }; + +// 导出坐标系转换工具 +export { default as coordinateTransform } from './coordinate-transform'; +export type { CoordinatePoint } from './coordinate-transform'; +export { + wgs84ToGcj02, + gcj02ToWgs84, + wgs84ToGcj02Point, + gcj02ToWgs84Point, + batchWgs84ToGcj02, + batchGcj02ToWgs84 +} from './coordinate-transform'; \ No newline at end of file