Files
xinlong-shop-taro/src/utils/geofence.ts
赵忠林 f3886664f7 fix(api): 替换所有接口请求的域名为 guilixu-api.websoft.top
- 将获取 STS Token 的接口地址由 paopao-api 改为 guilixu-api
- 更新图片上传接口地址为新的 guilixu-api 域名
- 修改用户推广页面中邀请码链接和二维码接口的域名
- 更改注册页微信登录接口请求的域名为 guilixu-api
2026-06-16 17:15:59 +08:00

132 lines
4.0 KiB
TypeScript

export type LngLat = { lng: number; lat: number }
function normalizeLngLat(a: number, b: number): LngLat | null {
if (!Number.isFinite(a) || !Number.isFinite(b)) return null
const looksLikeLngLat = Math.abs(a) <= 180 && Math.abs(b) <= 90
const looksLikeLatLng = Math.abs(a) <= 90 && Math.abs(b) <= 180
if (looksLikeLngLat) return { lng: a, lat: b }
if (looksLikeLatLng) return { lng: b, lat: a }
return null
}
function parsePointLike(v: any): LngLat | null {
if (!v) return null
if (Array.isArray(v) && v.length >= 2) {
return normalizeLngLat(Number(v[0]), Number(v[1]))
}
if (typeof v === 'object') {
const a = v.lng ?? v.lon ?? v.longitude ?? v.x
const b = v.lat ?? v.latitude ?? v.y
if (a !== undefined && b !== undefined) {
return normalizeLngLat(Number(a), Number(b))
}
}
if (typeof v === 'string') {
return parseLngLatFromText(v)
}
return null
}
export function parseLngLatFromText(raw: string | undefined): LngLat | null {
const text = (raw || '').trim()
if (!text) return null
const parts = text.split(/[,\s]+/).filter(Boolean)
if (parts.length < 2) return null
const a = parts[0]
const b = parts[1]
if (!a || !b) return null
return normalizeLngLat(parseFloat(a), parseFloat(b))
}
/**
* Parse fence "points" into a polygon point list.
*/
export function parseFencePoints(pointsRaw: string | undefined): LngLat[] {
const text = (pointsRaw || '').trim()
if (!text) return []
if (text.startsWith('[') || text.startsWith('{')) {
try {
const parsed = JSON.parse(text)
if (Array.isArray(parsed)) {
const list = parsed.map(parsePointLike).filter(Boolean) as LngLat[]
if (list.length) return list
if (Array.isArray(parsed[0])) {
const inner = (parsed[0] as any[]).map(parsePointLike).filter(Boolean) as LngLat[]
if (inner.length) return inner
}
}
} catch (_e) {
// fall through
}
}
const segments = text.split(/[;|\n\r]+/).map(s => s.trim()).filter(Boolean)
if (segments.length > 1) {
const list = segments.map(seg => {
const nums = seg.match(/-?\d+(\.\d+)?/g) || []
if (nums.length < 2) return null
const a = nums[0]
const b = nums[1]
if (!a || !b) return null
return normalizeLngLat(parseFloat(a), parseFloat(b))
}).filter(Boolean) as LngLat[]
if (list.length) return list
}
const nums = text.match(/-?\d+(\.\d+)?/g) || []
if (nums.length >= 6 && nums.length % 2 === 0) {
const list: LngLat[] = []
for (let i = 0; i < nums.length; i += 2) {
const a = nums[i]
const b = nums[i + 1]
if (!a || !b) continue
const p = normalizeLngLat(parseFloat(a), parseFloat(b))
if (p) list.push(p)
}
if (list.length) return list
}
return []
}
function pointOnSegment(p: LngLat, a: LngLat, b: LngLat, eps = 1e-9): boolean {
const cross = (b.lat - a.lat) * (p.lng - a.lng) - (b.lng - a.lng) * (p.lat - a.lat)
if (Math.abs(cross) > eps) return false
const dot = (p.lng - a.lng) * (b.lng - a.lng) + (p.lat - a.lat) * (b.lat - a.lat)
if (dot < -eps) return false
const lenSq = (b.lng - a.lng) ** 2 + (b.lat - a.lat) ** 2
return dot <= lenSq + eps
}
export function pointInPolygon(p: LngLat, polygon: LngLat[]): boolean {
if (!polygon || polygon.length < 3) return false
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const a = polygon[j]
const b = polygon[i]
if (pointOnSegment(p, a, b)) return true
}
let inside = false
const x = p.lng
const y = p.lat
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const xi = polygon[i].lng
const yi = polygon[i].lat
const xj = polygon[j].lng
const yj = polygon[j].lat
const intersect = (yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi
if (intersect) inside = !inside
}
return inside
}
export function pointInAnyPolygon(p: LngLat, polygons: LngLat[][]): boolean {
for (const poly of polygons) {
if (pointInPolygon(p, poly)) return true
}
return false
}