2026-07-01 08:48:39 +08:00
|
|
|
|
export type NearbyPointDisplayMode = 'default' | 'replace' | 'expand';
|
|
|
|
|
|
|
|
|
|
|
|
export type NearbyPointAutoRule = {
|
|
|
|
|
|
distanceThresholdMeters: number;
|
|
|
|
|
|
replaceAtZoom: number;
|
|
|
|
|
|
expandAtZoom: number;
|
|
|
|
|
|
expandOffsetPx: number;
|
|
|
|
|
|
angleStepDeg: number;
|
|
|
|
|
|
minGroupSize: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export type NearbyPointLayoutRule = {
|
|
|
|
|
|
ringSize: number;
|
|
|
|
|
|
fanAngleForTwoDeg: number;
|
|
|
|
|
|
fanAngleForFourDeg: number;
|
|
|
|
|
|
fanAngleForManyDeg: number;
|
|
|
|
|
|
ringGapPx: number;
|
|
|
|
|
|
ringGapFactor: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export type NearbyPointDensityDisplayRule = {
|
|
|
|
|
|
minDensityValue: number;
|
|
|
|
|
|
minZoom: number;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export type NearbyPointConfig = {
|
|
|
|
|
|
autoRule: NearbyPointAutoRule;
|
|
|
|
|
|
layoutRule: NearbyPointLayoutRule;
|
|
|
|
|
|
densityDisplayRules: NearbyPointDensityDisplayRule[];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export type NearbyPointRuntimeMeta = {
|
|
|
|
|
|
groupId: string;
|
|
|
|
|
|
displayName: string;
|
|
|
|
|
|
groupSize: number;
|
|
|
|
|
|
priority: number;
|
|
|
|
|
|
showZoomMin: number | null;
|
|
|
|
|
|
showZoomMax: number | null;
|
|
|
|
|
|
replaceAtZoom: number;
|
|
|
|
|
|
expandAtZoom: number;
|
|
|
|
|
|
expandOffsetPx: number;
|
|
|
|
|
|
expandAngleDeg: number;
|
|
|
|
|
|
displayMode: NearbyPointDisplayMode;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 备注:近邻点统一调参入口。
|
|
|
|
|
|
// 当前参数偏向“更容易成组、稍早进入替换与展开、展开位移更明显”,
|
|
|
|
|
|
// 方便抽吸效果在常用浏览层级更早被感知。
|
|
|
|
|
|
export const DEFAULT_NEARBY_POINT_AUTO_RULE: NearbyPointAutoRule = {
|
|
|
|
|
|
distanceThresholdMeters: 9000,
|
|
|
|
|
|
replaceAtZoom: 10.5,
|
|
|
|
|
|
expandAtZoom: 12.2,
|
|
|
|
|
|
expandOffsetPx: 48,
|
|
|
|
|
|
angleStepDeg: 55,
|
|
|
|
|
|
minGroupSize: 2
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 备注:展开布局统一调参入口。这里控制单圈数量、扇形角度和外圈扩散节奏。
|
|
|
|
|
|
export const DEFAULT_NEARBY_POINT_LAYOUT_RULE: NearbyPointLayoutRule = {
|
|
|
|
|
|
ringSize: 5,
|
|
|
|
|
|
fanAngleForTwoDeg: 110,
|
|
|
|
|
|
fanAngleForFourDeg: 150,
|
|
|
|
|
|
fanAngleForManyDeg: 180,
|
|
|
|
|
|
ringGapPx: 22,
|
|
|
|
|
|
ringGapFactor: 0.85
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 备注:密度分档显示规则。distance 越大表示越稀疏,越早参与显示候选。
|
|
|
|
|
|
export const DEFAULT_NEARBY_POINT_DENSITY_DISPLAY_RULES: NearbyPointDensityDisplayRule[] =
|
|
|
|
|
|
[
|
|
|
|
|
|
{
|
|
|
|
|
|
minDensityValue: 1500000,
|
|
|
|
|
|
minZoom: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
minDensityValue: 800000,
|
2026-07-14 16:09:01 +08:00
|
|
|
|
minZoom: 0
|
2026-07-01 08:48:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
minDensityValue: 400000,
|
2026-07-14 16:09:01 +08:00
|
|
|
|
minZoom: 0
|
2026-07-01 08:48:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
minDensityValue: 50000,
|
2026-07-14 16:09:01 +08:00
|
|
|
|
minZoom: 2.0
|
2026-07-01 08:48:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
minDensityValue: 0,
|
2026-07-14 16:09:01 +08:00
|
|
|
|
minZoom: 4.0
|
2026-07-01 08:48:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
export const DEFAULT_NEARBY_POINT_CONFIG: NearbyPointConfig = {
|
|
|
|
|
|
autoRule: DEFAULT_NEARBY_POINT_AUTO_RULE,
|
|
|
|
|
|
layoutRule: DEFAULT_NEARBY_POINT_LAYOUT_RULE,
|
|
|
|
|
|
densityDisplayRules: DEFAULT_NEARBY_POINT_DENSITY_DISPLAY_RULES
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const getNearbyPointConfig = (): NearbyPointConfig => {
|
|
|
|
|
|
return DEFAULT_NEARBY_POINT_CONFIG;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const getNearbyPointAutoRule = (): NearbyPointAutoRule => {
|
|
|
|
|
|
return getNearbyPointConfig().autoRule;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const getNearbyPointLayoutRule = (): NearbyPointLayoutRule => {
|
|
|
|
|
|
return getNearbyPointConfig().layoutRule;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const getNearbyPointDensityDisplayRules =
|
|
|
|
|
|
(): NearbyPointDensityDisplayRule[] => {
|
|
|
|
|
|
return getNearbyPointConfig().densityDisplayRules;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
type GroupCandidate = {
|
|
|
|
|
|
index: number;
|
|
|
|
|
|
point: Record<string, any>;
|
|
|
|
|
|
lon: number;
|
|
|
|
|
|
lat: number;
|
|
|
|
|
|
layerKey: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-14 16:09:01 +08:00
|
|
|
|
const isAlarmRangeLegendState = (legendState: string): boolean => {
|
|
|
|
|
|
return (
|
|
|
|
|
|
legendState.startsWith('alarm_range_') ||
|
|
|
|
|
|
legendState.startsWith('large_eng_built_alarm_range_') ||
|
|
|
|
|
|
legendState.startsWith('mid_eng_built_alarm_range_')
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getPointRenderPriority = (point: Record<string, any>): number => {
|
|
|
|
|
|
const layerKey = String(point.layerKey || point.type || '').toLowerCase();
|
|
|
|
|
|
const legendState = String(point.anchoPointState || '').trim().toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
layerKey.includes('eng_alarm_point') ||
|
|
|
|
|
|
layerKey.includes('alarm_range') ||
|
|
|
|
|
|
isAlarmRangeLegendState(legendState)
|
|
|
|
|
|
) {
|
|
|
|
|
|
return 300;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
layerKey.includes('eng_point') ||
|
|
|
|
|
|
legendState.startsWith('large_eng_') ||
|
|
|
|
|
|
legendState.startsWith('mid_eng_')
|
|
|
|
|
|
) {
|
|
|
|
|
|
return 200;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 100;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const normalizeText = (value: unknown): string => {
|
|
|
|
|
|
return String(value ?? '')
|
|
|
|
|
|
.trim()
|
|
|
|
|
|
.replace(/[()()]/g, '')
|
|
|
|
|
|
.toLowerCase();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const toFiniteNumber = (value: unknown): number | null => {
|
|
|
|
|
|
if (value === '' || value === null || value === undefined) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
const numericValue = Number(value);
|
|
|
|
|
|
return Number.isFinite(numericValue) ? numericValue : null;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const haversineDistanceMeters = (
|
|
|
|
|
|
lon1: number,
|
|
|
|
|
|
lat1: number,
|
|
|
|
|
|
lon2: number,
|
|
|
|
|
|
lat2: number
|
|
|
|
|
|
): number => {
|
|
|
|
|
|
const toRad = (degree: number) => (degree * Math.PI) / 180;
|
|
|
|
|
|
const earthRadius = 6371000;
|
|
|
|
|
|
const dLat = toRad(lat2 - lat1);
|
|
|
|
|
|
const dLon = toRad(lon2 - lon1);
|
|
|
|
|
|
const lat1Rad = toRad(lat1);
|
|
|
|
|
|
const lat2Rad = toRad(lat2);
|
|
|
|
|
|
|
|
|
|
|
|
const a =
|
|
|
|
|
|
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
|
|
|
|
Math.cos(lat1Rad) *
|
|
|
|
|
|
Math.cos(lat2Rad) *
|
|
|
|
|
|
Math.sin(dLon / 2) *
|
|
|
|
|
|
Math.sin(dLon / 2);
|
|
|
|
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
|
|
|
|
return earthRadius * c;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const clearNearbyPointRuntimeMeta = (point: Record<string, any>) => {
|
|
|
|
|
|
delete point._nearbyRule;
|
|
|
|
|
|
delete point._nearbyGroupId;
|
|
|
|
|
|
delete point._nearbyDisplayName;
|
|
|
|
|
|
delete point._nearbyGroupSize;
|
|
|
|
|
|
delete point._nearbyPriority;
|
|
|
|
|
|
delete point._nearbyShowZoomMin;
|
|
|
|
|
|
delete point._nearbyShowZoomMax;
|
|
|
|
|
|
delete point._nearbyReplaceAtZoom;
|
|
|
|
|
|
delete point._nearbyExpandAtZoom;
|
|
|
|
|
|
delete point._nearbyExpandOffsetPx;
|
|
|
|
|
|
delete point._nearbyExpandAngleDeg;
|
|
|
|
|
|
delete point._nearbyDisplayMode;
|
|
|
|
|
|
delete point._nearbyIsPrimary;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getPointDisplayName = (point: Record<string, any>): string => {
|
|
|
|
|
|
return (
|
|
|
|
|
|
point.titleName || point.stnm || point.ennm || point.stcd || point._id || ''
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getPointStableIdentity = (
|
|
|
|
|
|
point: Record<string, any>,
|
|
|
|
|
|
index: number
|
|
|
|
|
|
): string => {
|
|
|
|
|
|
return normalizeText(
|
|
|
|
|
|
point.stcd ||
|
|
|
|
|
|
point._id ||
|
|
|
|
|
|
point.titleName ||
|
|
|
|
|
|
point.stnm ||
|
|
|
|
|
|
point.ennm ||
|
|
|
|
|
|
`point_${index}`
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getGroupSortKey = (candidate: GroupCandidate): string => {
|
|
|
|
|
|
return [
|
|
|
|
|
|
getPointStableIdentity(candidate.point, candidate.index),
|
|
|
|
|
|
normalizeText(candidate.point.layerKey),
|
|
|
|
|
|
String(candidate.index).padStart(6, '0')
|
|
|
|
|
|
].join('|');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const compareGroupCandidates = (
|
|
|
|
|
|
left: GroupCandidate,
|
|
|
|
|
|
right: GroupCandidate
|
|
|
|
|
|
): number => {
|
2026-07-14 16:09:01 +08:00
|
|
|
|
const leftRenderPriority = getPointRenderPriority(left.point);
|
|
|
|
|
|
const rightRenderPriority = getPointRenderPriority(right.point);
|
|
|
|
|
|
if (leftRenderPriority !== rightRenderPriority) {
|
|
|
|
|
|
return rightRenderPriority - leftRenderPriority;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const leftDensityValue = toFiniteNumber(left.point.distance);
|
|
|
|
|
|
const rightDensityValue = toFiniteNumber(right.point.distance);
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
leftDensityValue !== null &&
|
|
|
|
|
|
rightDensityValue !== null &&
|
|
|
|
|
|
leftDensityValue !== rightDensityValue
|
|
|
|
|
|
) {
|
|
|
|
|
|
return rightDensityValue - leftDensityValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (leftDensityValue !== null || rightDensityValue !== null) {
|
|
|
|
|
|
return leftDensityValue !== null ? -1 : 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return getGroupSortKey(left).localeCompare(getGroupSortKey(right));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const buildRuntimeMeta = (
|
|
|
|
|
|
groupId: string,
|
|
|
|
|
|
groupSize: number,
|
|
|
|
|
|
priority: number,
|
|
|
|
|
|
displayName: string,
|
|
|
|
|
|
rule: NearbyPointAutoRule
|
|
|
|
|
|
): NearbyPointRuntimeMeta => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
groupId,
|
|
|
|
|
|
displayName,
|
|
|
|
|
|
groupSize,
|
|
|
|
|
|
priority,
|
2026-07-14 16:09:01 +08:00
|
|
|
|
showZoomMin: null,
|
2026-07-01 08:48:39 +08:00
|
|
|
|
showZoomMax: null,
|
|
|
|
|
|
replaceAtZoom: rule.replaceAtZoom,
|
|
|
|
|
|
expandAtZoom: rule.expandAtZoom,
|
|
|
|
|
|
expandOffsetPx: rule.expandOffsetPx,
|
2026-07-14 16:09:01 +08:00
|
|
|
|
expandAngleDeg: 0,
|
|
|
|
|
|
displayMode: 'default'
|
2026-07-01 08:48:39 +08:00
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const buildGroupCandidates = (
|
|
|
|
|
|
points: Record<string, any>[] = []
|
|
|
|
|
|
): GroupCandidate[] => {
|
|
|
|
|
|
const candidates: GroupCandidate[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
points.forEach((point, index) => {
|
|
|
|
|
|
const lon = toFiniteNumber(point.lgtd);
|
|
|
|
|
|
const lat = toFiniteNumber(point.lttd);
|
|
|
|
|
|
if (lon === null || lat === null) {
|
|
|
|
|
|
clearNearbyPointRuntimeMeta(point);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Math.abs(lon) > 180 || Math.abs(lat) > 90) {
|
|
|
|
|
|
clearNearbyPointRuntimeMeta(point);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
candidates.push({
|
|
|
|
|
|
index,
|
|
|
|
|
|
point,
|
|
|
|
|
|
lon,
|
|
|
|
|
|
lat,
|
|
|
|
|
|
layerKey: String(point.layerKey || '')
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return candidates;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-14 16:09:01 +08:00
|
|
|
|
const buildNearbyGroups = (
|
2026-07-01 08:48:39 +08:00
|
|
|
|
candidates: GroupCandidate[],
|
|
|
|
|
|
rule: NearbyPointAutoRule
|
|
|
|
|
|
): GroupCandidate[][] => {
|
|
|
|
|
|
const result: GroupCandidate[][] = [];
|
2026-07-14 16:09:01 +08:00
|
|
|
|
const sortedCandidates = [...candidates].sort(compareGroupCandidates);
|
|
|
|
|
|
const consumedIndexes = new Set<number>();
|
2026-07-01 08:48:39 +08:00
|
|
|
|
|
2026-07-14 16:09:01 +08:00
|
|
|
|
sortedCandidates.forEach((seedCandidate, seedIndex) => {
|
|
|
|
|
|
if (consumedIndexes.has(seedIndex)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-01 08:48:39 +08:00
|
|
|
|
|
2026-07-14 16:09:01 +08:00
|
|
|
|
const groupItems: GroupCandidate[] = [seedCandidate];
|
|
|
|
|
|
const groupIndexes = [seedIndex];
|
2026-07-01 08:48:39 +08:00
|
|
|
|
|
2026-07-14 16:09:01 +08:00
|
|
|
|
for (
|
|
|
|
|
|
let candidateIndex = seedIndex + 1;
|
|
|
|
|
|
candidateIndex < sortedCandidates.length;
|
|
|
|
|
|
candidateIndex += 1
|
|
|
|
|
|
) {
|
|
|
|
|
|
if (consumedIndexes.has(candidateIndex)) {
|
|
|
|
|
|
continue;
|
2026-07-01 08:48:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-14 16:09:01 +08:00
|
|
|
|
const currentCandidate = sortedCandidates[candidateIndex];
|
|
|
|
|
|
const distanceMeters = haversineDistanceMeters(
|
|
|
|
|
|
seedCandidate.lon,
|
|
|
|
|
|
seedCandidate.lat,
|
|
|
|
|
|
currentCandidate.lon,
|
|
|
|
|
|
currentCandidate.lat
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (distanceMeters <= rule.distanceThresholdMeters) {
|
|
|
|
|
|
groupItems.push(currentCandidate);
|
|
|
|
|
|
groupIndexes.push(candidateIndex);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
}
|
2026-07-14 16:09:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (groupItems.length >= rule.minGroupSize) {
|
|
|
|
|
|
groupIndexes.forEach(index => consumedIndexes.add(index));
|
|
|
|
|
|
result.push(groupItems);
|
|
|
|
|
|
}
|
2026-07-01 08:48:39 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 备注:统一按空间距离自动识别近邻点组,不按名称或点类型分规则。
|
|
|
|
|
|
export const attachNearbyPointRuntimeMeta = (
|
|
|
|
|
|
points: Record<string, any>[] = [],
|
|
|
|
|
|
rule: NearbyPointAutoRule = getNearbyPointAutoRule()
|
|
|
|
|
|
): Record<string, any>[] => {
|
|
|
|
|
|
if (!Array.isArray(points) || points.length === 0) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
points.forEach(point => clearNearbyPointRuntimeMeta(point));
|
|
|
|
|
|
|
|
|
|
|
|
const candidates = buildGroupCandidates(points);
|
|
|
|
|
|
if (candidates.length < rule.minGroupSize) {
|
|
|
|
|
|
return points;
|
|
|
|
|
|
}
|
2026-07-14 16:09:01 +08:00
|
|
|
|
const groupedCandidates = buildNearbyGroups(candidates, rule);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
|
|
|
|
|
|
let groupSeed = 0;
|
|
|
|
|
|
groupedCandidates.forEach(groupItems => {
|
|
|
|
|
|
if (groupItems.length < rule.minGroupSize) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const sortedItems = [...groupItems].sort(compareGroupCandidates);
|
|
|
|
|
|
groupSeed += 1;
|
|
|
|
|
|
const groupId = `nearby-group-${groupSeed}`;
|
|
|
|
|
|
|
|
|
|
|
|
sortedItems.forEach((candidate, orderIndex) => {
|
|
|
|
|
|
const priority = orderIndex + 1;
|
|
|
|
|
|
const runtimeMeta = buildRuntimeMeta(
|
|
|
|
|
|
groupId,
|
|
|
|
|
|
sortedItems.length,
|
|
|
|
|
|
priority,
|
|
|
|
|
|
getPointDisplayName(candidate.point),
|
|
|
|
|
|
rule
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
candidate.point._nearbyRule = runtimeMeta;
|
|
|
|
|
|
candidate.point._nearbyGroupId = runtimeMeta.groupId;
|
|
|
|
|
|
candidate.point._nearbyDisplayName = runtimeMeta.displayName;
|
|
|
|
|
|
candidate.point._nearbyGroupSize = runtimeMeta.groupSize;
|
|
|
|
|
|
candidate.point._nearbyPriority = runtimeMeta.priority;
|
|
|
|
|
|
candidate.point._nearbyShowZoomMin = runtimeMeta.showZoomMin;
|
|
|
|
|
|
candidate.point._nearbyShowZoomMax = runtimeMeta.showZoomMax;
|
|
|
|
|
|
candidate.point._nearbyReplaceAtZoom = runtimeMeta.replaceAtZoom;
|
|
|
|
|
|
candidate.point._nearbyExpandAtZoom = runtimeMeta.expandAtZoom;
|
|
|
|
|
|
candidate.point._nearbyExpandOffsetPx = runtimeMeta.expandOffsetPx;
|
|
|
|
|
|
candidate.point._nearbyExpandAngleDeg = runtimeMeta.expandAngleDeg;
|
|
|
|
|
|
candidate.point._nearbyDisplayMode = runtimeMeta.displayMode;
|
|
|
|
|
|
candidate.point._nearbyIsPrimary = runtimeMeta.priority === 1;
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return points;
|
|
|
|
|
|
};
|