570fbeb537
Co-authored-by: Cursor <cursoragent@cursor.com>
141 lines
4.2 KiB
JavaScript
141 lines
4.2 KiB
JavaScript
export const coordinateInputPattern = /^-?\d+(\.\d{0,6})?$/;
|
||
|
||
const coordinateRanges = {
|
||
longitude: {
|
||
label: '经度',
|
||
min: -180,
|
||
max: 180,
|
||
},
|
||
latitude: {
|
||
label: '纬度',
|
||
min: -90,
|
||
max: 90,
|
||
},
|
||
};
|
||
|
||
/** WGS-84 → GCJ-02(高德)偏移算法常量 */
|
||
const WGS84_TO_GCJ02_PI = Math.PI;
|
||
const WGS84_TO_GCJ02_A = 6378245.0;
|
||
const WGS84_TO_GCJ02_EE = 0.00669342162296594323;
|
||
|
||
const isOutOfChina = (longitude, latitude) =>
|
||
longitude < 72.004 || longitude > 137.8347 || latitude < 0.8293 || latitude > 55.8271;
|
||
|
||
const transformLatitude = (longitude, latitude) => {
|
||
let result =
|
||
-100.0 +
|
||
2.0 * longitude +
|
||
3.0 * latitude +
|
||
0.2 * latitude * latitude +
|
||
0.1 * longitude * latitude +
|
||
0.2 * Math.sqrt(Math.abs(longitude));
|
||
result +=
|
||
((20.0 * Math.sin(6.0 * longitude * WGS84_TO_GCJ02_PI) +
|
||
20.0 * Math.sin(2.0 * longitude * WGS84_TO_GCJ02_PI)) *
|
||
2.0) /
|
||
3.0;
|
||
result +=
|
||
((20.0 * Math.sin(latitude * WGS84_TO_GCJ02_PI) +
|
||
40.0 * Math.sin((latitude / 3.0) * WGS84_TO_GCJ02_PI)) *
|
||
2.0) /
|
||
3.0;
|
||
result +=
|
||
((160.0 * Math.sin((latitude / 12.0) * WGS84_TO_GCJ02_PI) +
|
||
320 * Math.sin((latitude * WGS84_TO_GCJ02_PI) / 30.0)) *
|
||
2.0) /
|
||
3.0;
|
||
return result;
|
||
};
|
||
|
||
const transformLongitude = (longitude, latitude) => {
|
||
let result =
|
||
300.0 +
|
||
longitude +
|
||
2.0 * latitude +
|
||
0.1 * longitude * longitude +
|
||
0.1 * longitude * latitude +
|
||
0.1 * Math.sqrt(Math.abs(longitude));
|
||
result +=
|
||
((20.0 * Math.sin(6.0 * longitude * WGS84_TO_GCJ02_PI) +
|
||
20.0 * Math.sin(2.0 * longitude * WGS84_TO_GCJ02_PI)) *
|
||
2.0) /
|
||
3.0;
|
||
result +=
|
||
((20.0 * Math.sin(longitude * WGS84_TO_GCJ02_PI) +
|
||
40.0 * Math.sin((longitude / 3.0) * WGS84_TO_GCJ02_PI)) *
|
||
2.0) /
|
||
3.0;
|
||
result +=
|
||
((150.0 * Math.sin((longitude / 12.0) * WGS84_TO_GCJ02_PI) +
|
||
300.0 * Math.sin((longitude / 30.0) * WGS84_TO_GCJ02_PI)) *
|
||
2.0) /
|
||
3.0;
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* WGS-84(GPS / LBS)转 GCJ-02(高德/国测局),供高德地图绘制使用。
|
||
* 中国境外坐标不做偏移,原样返回。
|
||
* @param {number} longitude
|
||
* @param {number} latitude
|
||
* @returns {[number, number]} [经度, 纬度]
|
||
*/
|
||
export const wgs84ToGcj02 = (longitude, latitude) => {
|
||
const lng = Number(longitude);
|
||
const lat = Number(latitude);
|
||
if (!Number.isFinite(lng) || !Number.isFinite(lat) || isOutOfChina(lng, lat)) {
|
||
return [lng, lat];
|
||
}
|
||
let dLat = transformLatitude(lng - 105.0, lat - 35.0);
|
||
let dLng = transformLongitude(lng - 105.0, lat - 35.0);
|
||
const radLat = (lat / 180.0) * WGS84_TO_GCJ02_PI;
|
||
let magic = Math.sin(radLat);
|
||
magic = 1 - WGS84_TO_GCJ02_EE * magic * magic;
|
||
const sqrtMagic = Math.sqrt(magic);
|
||
dLat =
|
||
(dLat * 180.0) /
|
||
(((WGS84_TO_GCJ02_A * (1 - WGS84_TO_GCJ02_EE)) / (magic * sqrtMagic)) * WGS84_TO_GCJ02_PI);
|
||
dLng = (dLng * 180.0) / ((WGS84_TO_GCJ02_A / sqrtMagic) * Math.cos(radLat) * WGS84_TO_GCJ02_PI);
|
||
return [lng + dLng, lat + dLat];
|
||
};
|
||
|
||
export const normalizeCoordinateInput = value => {
|
||
const rawValue = String(value ?? '').replace(/[^\d.-]/g, '');
|
||
const isNegative = rawValue.startsWith('-');
|
||
const unsignedValue = rawValue.replace(/-/g, '');
|
||
if (!unsignedValue) {
|
||
return isNegative ? '-' : '';
|
||
}
|
||
const [integerPart, ...decimalParts] = unsignedValue.split('.');
|
||
const sign = isNegative ? '-' : '';
|
||
if (!decimalParts.length) {
|
||
return `${sign}${integerPart}`;
|
||
}
|
||
return `${sign}${integerPart || '0'}.${decimalParts.join('').slice(0, 6)}`;
|
||
};
|
||
|
||
export const isCoordinateInput = value => {
|
||
if (value === undefined || value === null || value === '') {
|
||
return true;
|
||
}
|
||
return coordinateInputPattern.test(String(value));
|
||
};
|
||
|
||
export const getCoordinateValidationMessage = (value, type, required = false) => {
|
||
const range = coordinateRanges[type];
|
||
if (!range) {
|
||
return '';
|
||
}
|
||
if (value === undefined || value === null || value === '') {
|
||
return required ? `${range.label}不能为空` : '';
|
||
}
|
||
if (!isCoordinateInput(value)) {
|
||
return `${range.label}最多保留6位小数`;
|
||
}
|
||
const numberValue = Number(value);
|
||
if (numberValue < range.min || numberValue > range.max) {
|
||
return `${range.label}范围为 ${range.min} 到 ${range.max}`;
|
||
}
|
||
return '';
|
||
};
|