atchain-mapbox-vue
Version:
A Vue 3 MapBox component library with subway lines, stations, markers and polygons support. Zero dependencies except Vue 3 and Mapbox GL JS.
185 lines (160 loc) • 4.29 kB
text/typescript
/**
* MapBox 工具函数
*/
import type { Position } from 'geojson'
import { EARTH_RADIUS_KM } from './useMapBoxConstants'
/**
* 角度转弧度
*/
export const toRadians = (value: number): number => (value * Math.PI) / 180
/**
* 计算两点间的距离(公里)
* 使用 Haversine 公式
*/
export const haversineDistanceKm = (coordA: Position, coordB: Position): number => {
const [lngA, latA] = coordA as [number, number]
const [lngB, latB] = coordB as [number, number]
const dLat = toRadians(latB - latA)
const dLng = toRadians(lngB - lngA)
const lat1 = toRadians(latA)
const lat2 = toRadians(latB)
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.sin(dLng / 2) * Math.sin(dLng / 2) * Math.cos(lat1) * Math.cos(lat2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return EARTH_RADIUS_KM * c
}
/**
* 检查坐标是否在指定中心点和半径范围内
*/
export const isWithinRadius = (
coord: Position,
center: Position,
radiusKm: number
): boolean => {
return haversineDistanceKm(coord, center) <= radiusKm
}
/**
* 过滤在指定范围内的要素
*/
export const filterFeaturesByRadius = <T extends { geometry: { coordinates: Position } }>(
features: T[],
center: Position,
radiusKm: number
): T[] => {
return features.filter(feature =>
isWithinRadius(feature.geometry.coordinates, center, radiusKm)
)
}
/**
* 延时执行函数
*/
export const delay = (ms: number): Promise<void> => {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* 安全的 JSON 解析
*/
export const safeJsonParse = (jsonString: string, fallback: any = null): any => {
try {
return JSON.parse(jsonString)
} catch (error) {
console.warn('JSON 解析失败:', error)
return fallback
}
}
/**
* 深度合并对象
*/
export const deepMerge = <T extends Record<string, any>>(target: T, source: Partial<T>): T => {
const result = { ...target }
for (const key in source) {
if (source.hasOwnProperty(key)) {
const sourceValue = source[key]
const targetValue = result[key]
if (
sourceValue &&
typeof sourceValue === 'object' &&
!Array.isArray(sourceValue) &&
targetValue &&
typeof targetValue === 'object' &&
!Array.isArray(targetValue)
) {
result[key] = deepMerge(targetValue, sourceValue)
} else {
result[key] = sourceValue as T[Extract<keyof T, string>]
}
}
}
return result
}
/**
* 防抖函数
*/
export const debounce = <T extends (...args: any[]) => any>(
func: T,
wait: number
): ((...args: Parameters<T>) => void) => {
let timeout: number | null = null
return (...args: Parameters<T>) => {
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
func(...args)
}, wait)
}
}
/**
* 节流函数
*/
export const throttle = <T extends (...args: any[]) => any>(
func: T,
wait: number
): ((...args: Parameters<T>) => void) => {
let lastTime = 0
return (...args: Parameters<T>) => {
const now = Date.now()
if (now - lastTime >= wait) {
lastTime = now
func(...args)
}
}
}
/**
* 生成唯一 ID
*/
export const generateId = (prefix: string = 'mapbox'): string => {
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
}
/**
* 验证坐标格式
*/
export const isValidCoordinate = (coord: any): coord is [number, number] => {
return (
Array.isArray(coord) &&
coord.length === 2 &&
typeof coord[0] === 'number' &&
typeof coord[1] === 'number' &&
!isNaN(coord[0]) &&
!isNaN(coord[1]) &&
coord[0] >= -180 &&
coord[0] <= 180 &&
coord[1] >= -90 &&
coord[1] <= 90
)
}
/**
* 验证 GeoJSON 要素
*/
export const isValidGeoJSONFeature = (feature: any): boolean => {
return (
feature &&
typeof feature === 'object' &&
feature.type === 'Feature' &&
feature.geometry &&
typeof feature.geometry === 'object' &&
feature.geometry.type &&
feature.geometry.coordinates
)
}