| | |
| | | }; |
| | | |
| | | /** |
| | | * 根据站点空间分布估算推荐缩放等级(站点越分散,缩放越小)。 |
| | | * 根据站点空间分布估算推荐缩放等级;如提供中心点,则优先保证从中心点往外至少可见 3 个点位。 |
| | | */ |
| | | export const getAutoScaleBySites = (sites: any[]) => { |
| | | export const getAutoScaleBySites = (sites: any[], centerLat?: number, centerLng?: number) => { |
| | | if (!Array.isArray(sites) || sites.length === 0) return null; |
| | | const validSites = sites |
| | | .map((site) => ({ |
| | |
| | | return 17; |
| | | }; // 统一距离到缩放等级的映射规则 |
| | | |
| | | const hasValidCenter = Number.isFinite(Number(centerLat)) && Number.isFinite(Number(centerLng)); // 是否提供了有效中心点 |
| | | |
| | | /** |
| | | * 基于中心点计算至少可见 3 个点位所需缩放等级。 |
| | | */ |
| | | const resolveScaleByCenterVisibleSites = () => { |
| | | if (!hasValidCenter) return null; |
| | | const visibleSiteCount = Math.min(validSites.length, 3); // 最多按最近 3 个点位估算 |
| | | if (visibleSiteCount <= 1) return 16; |
| | | const sortedDistances = validSites |
| | | .map((site) => getDistance(Number(centerLat), Number(centerLng), site.latitude, site.longitude)) |
| | | .filter((distance) => Number.isFinite(distance)) |
| | | .sort((a, b) => a - b); |
| | | const targetRadius = sortedDistances[visibleSiteCount - 1]; // 取第 N 个最近点的半径,保证中心点往外至少覆盖 N 个点 |
| | | if (!Number.isFinite(targetRadius)) return null; |
| | | const safeDiagonal = Math.max(targetRadius * 2 * 1.35, 300); // 预留边缘与卡片遮挡余量,避免第 3 个点卡边 |
| | | return resolveScaleByDiagonal(safeDiagonal); |
| | | }; |
| | | |
| | | const centerVisibleScale = resolveScaleByCenterVisibleSites(); // 中心点口径下的推荐缩放等级 |
| | | if (typeof centerVisibleScale === 'number') { |
| | | return centerVisibleScale; |
| | | } |
| | | |
| | | /** |
| | | * 计算稀疏场景下“至少可见 3 个点位”所需的最大安全缩放等级。 |
| | | */ |
| | | const resolveSparseScaleForThreeVisibleSites = () => { |
| | | if (validSites.length < 3) return null; |
| | | const radiusNeedForThreeSites = validSites.reduce((maxRadius, baseSite) => { |
| | | const neighborDistances = validSites |
| | | .filter((site) => site !== baseSite) |
| | | .map((site) => getDistance(baseSite.latitude, baseSite.longitude, site.latitude, site.longitude)) |
| | | .sort((a, b) => a - b); |
| | | const radius = neighborDistances[1]; // 取第 2 个邻居距离,保证当前点周围至少覆盖 3 个点 |
| | | if (!Number.isFinite(radius)) return maxRadius; |
| | | return Math.max(maxRadius, radius); |
| | | }, 0); // 取所有基点中的最大值,保证任意基点都能满足 3 点可见 |
| | | if (radiusNeedForThreeSites <= 0) return null; |
| | | return resolveScaleByDiagonal(radiusNeedForThreeSites * 2); |
| | | }; |
| | | |
| | | /** |
| | | * 计算高密度场景下“精细视角至少可见 3 个点”的推荐缩放等级。 |
| | | */ |
| | | const resolveDenseScaleForThreeVisibleSites = () => { |
| | | if (validSites.length < 3) return null; |
| | | const denseAnchor = validSites.reduce((bestSite, currentSite) => { |
| | | if (!bestSite) return currentSite; |
| | | const currentNeighborDistances = validSites |
| | | .filter((site) => site !== currentSite) |
| | | .map((site) => getDistance(currentSite.latitude, currentSite.longitude, site.latitude, site.longitude)) |
| | | .sort((a, b) => a - b); |
| | | const bestNeighborDistances = validSites |
| | | .filter((site) => site !== bestSite) |
| | | .map((site) => getDistance(bestSite.latitude, bestSite.longitude, site.latitude, site.longitude)) |
| | | .sort((a, b) => a - b); |
| | | const currentRadius = currentNeighborDistances[1]; |
| | | const bestRadius = bestNeighborDistances[1]; |
| | | if (!Number.isFinite(bestRadius)) return currentSite; |
| | | if (!Number.isFinite(currentRadius)) return bestSite; |
| | | return currentRadius < bestRadius ? currentSite : bestSite; |
| | | }, null as any); // 选择“第 3 个点最近”的簇中心,避免被远端离散点拉远 |
| | | if (!denseAnchor) return null; |
| | | const denseNeighborDistances = validSites |
| | | .filter((site) => site !== denseAnchor) |
| | | .map((site) => getDistance(denseAnchor.latitude, denseAnchor.longitude, site.latitude, site.longitude)) |
| | | .sort((a, b) => a - b); |
| | | const denseRadius = denseNeighborDistances[1]; |
| | | if (!Number.isFinite(denseRadius) || denseRadius <= 0) return null; |
| | | return resolveScaleByDiagonal(denseRadius * 2); |
| | | }; |
| | | |
| | | const sparseScaleForThreeVisibleSites = resolveSparseScaleForThreeVisibleSites(); // 稀疏场景至少可见 3 点的最大缩放等级 |
| | | const denseScaleForThreeVisibleSites = resolveDenseScaleForThreeVisibleSites(); // 高密度场景局部精细视角的缩放等级 |
| | | |
| | | const nearbyCounts = validSites.map((targetSite, targetIndex) => { |
| | | return validSites.reduce((count, compareSite, compareIndex) => { |
| | | if (targetIndex === compareIndex) return count; // 密度统计不包含自身 |
| | |
| | | }); // 每个点位在 1km 半径内的“邻居点”数量 |
| | | const maxNearbyCount = Math.max(...nearbyCounts); |
| | | |
| | | // 高密度区:1km 内点位 > 5(按邻居数统计),固定精细视角。 |
| | | if (maxNearbyCount > 5) return 17; |
| | | // 高密度区:1km 内点位 >= 2(按邻居数统计),固定精细视角。 |
| | | if (maxNearbyCount >= 2) { |
| | | if (typeof denseScaleForThreeVisibleSites === 'number') { |
| | | return Math.min(17, denseScaleForThreeVisibleSites); |
| | | } |
| | | return 17; |
| | | } |
| | | |
| | | // 低密度区:1km 内点位 < 2,自动缩小到至少可见 3 个点位。 |
| | | if (maxNearbyCount < 2 && validSites.length >= 3) { |
| | | const radiusNeedForThreePoints = validSites.reduce((maxRadius, baseSite) => { |
| | | const neighborDistances = validSites |
| | | .filter((site) => site !== baseSite) |
| | | .map((site) => getDistance(baseSite.latitude, baseSite.longitude, site.latitude, site.longitude)) |
| | | .sort((a, b) => a - b); |
| | | const radius = neighborDistances[1]; // 包含 2 个邻居时即可在屏幕中达到 3 个点位 |
| | | if (!Number.isFinite(radius)) return maxRadius; |
| | | return Math.max(maxRadius, radius); |
| | | }, 0); // 取最大半径,确保低密度点位也能满足“至少 3 点可见” |
| | | if (radiusNeedForThreePoints > 0) { |
| | | return resolveScaleByDiagonal(radiusNeedForThreePoints * 2); |
| | | if (maxNearbyCount < 2) { |
| | | if (typeof sparseScaleForThreeVisibleSites === 'number') { |
| | | return sparseScaleForThreeVisibleSites; |
| | | } |
| | | } |
| | | |