Cesium 添加加载聚合点信息
/**
* 加载聚合点数据
*/
const aggregationPoint = async () => {
if (!viewer.value) return;
try {
// 请求GeoJSON数据
const response = await geoserver.get("/geoserver/czc/ows", {
params: {
service: "WFS",
version: "1.1.0",
request: "GetFeature",
typeName: "czc:gz_POI",
outputFormat: "application/json",
srsName: "EPSG:4326",
},
});
console.log(response, "response");
// 配置显示规则
const displayRules = {
1: {
showRange: [0, 10000],
imageUrl: location08, // 1. 村名 TYPE=1
},
2: {
showRange: [0, 4000],
imageUrl: `data:image/svg+xml,${encodeURIComponent(`<svg t="1754727042072" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="36892" width="200" height="200"><path d="M356.503704 925.392593a151.703704 47.407407 0 1 0 303.407407 0 151.703704 47.407407 0 1 0-303.407407 0Z" fill="#F9D523" p-id="36893"></path><path d="M877.985185 417.185185c0 189.62963-263.585185 422.874074-343.229629 489.244445-13.274074 11.377778-32.237037 11.377778-45.511112 0C409.6 840.059259 146.014815 606.814815 146.014815 417.185185 146.014815 214.281481 309.096296 51.2 512 51.2S877.985185 214.281481 877.985185 417.185185z" fill="#EF6926" p-id="36894"></path><path d="M512 373.57037m-106.192593 0a106.192593 106.192593 0 1 0 212.385186 0 106.192593 106.192593 0 1 0-212.385186 0Z" fill="#FFFFFF" p-id="36895"></path></svg>`)}`, // 2. 自然村(蓝色圆形) TYPE=2
},
3: {
showRange: [0, 2500],
imageUrl: location10, // 3. 企业 TYPE=3
},
4: {
showRange: [0, 2000],
imageUrl: location11, // 4. 商店、超市、理发店、饭店一类 TYPE=4
},
5: {
showRange: [0, 10000],
imageUrl: `data:image/svg+xml,${encodeURIComponent(`<svg t="1754725696188" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12068" width="200" height="200"><path d="M777.980507 828.372086v0.00998H631.267057v-61.160546l146.71345 0.249513v0.029942c0.59883 0 1.197661-0.019961 1.796491-0.019961l13.174269 0.019961v-0.658714A171.664717 171.664717 0 0 0 777.980507 424.17154c-5.070097 0-10.080312 0.229552-15.030643 0.658713C751.172865 293.536686 640.868304 190.62768 506.510721 190.62768c-142.211244 0-257.497076 115.284834-257.497076 257.497076a259.332491 259.332491 0 0 0 3.183782 40.520858A140.736125 140.736125 0 1 0 216.077973 766.393138v0.109786l151.703703 0.259493V828.382066H218.074074v-0.129746c-110.646893-1.447173-199.899571-91.581131-199.899571-202.564367 0-101.42191 74.510472-185.427836 171.773505-200.27883C201.595259 260.73076 338.865154 130.744639 506.510721 130.744639c146.50386 0 269.803041 99.276101 306.351657 234.212554C924.773801 382.003899 1010.526316 478.655127 1010.526316 595.337232c0 128.538947-104.066745 232.76538-232.545809 233.034854zM326.861598 581.863548a19.961014 19.961014 0 0 1 19.923088-19.961014H653.264094a19.934066 19.934066 0 0 1 14.77115 33.304951L517.259727 844.770058c-0.019961-0.009981-0.049903-0.019961-0.069863-0.029941a19.953029 19.953029 0 0 1-35.237178-1.337388l-149.960109-248.215205A19.847236 19.847236 0 0 1 326.861598 581.863548z m173.161794 189.63961L595.337232 611.805068H404.709552z" fill="#E61D15" p-id="12069"></path><path d="M777.481481 828.382066c-1.497076 0-2.994152-0.029942-4.491228-0.059883v-60.901052c1.656764 0.049903 3.323509 0.079844 4.990254 0.079844a171.664717 171.664717 0 1 0-131.123899-282.448343h-74.714074A233.002916 233.002916 0 0 1 777.481481 362.292398c128.708616 0 233.044834 104.336218 233.044835 233.044834S906.190097 828.382066 777.481481 828.382066z" fill="#E61D15" p-id="12070"></path></svg>`)}`, // 5. 村名字 TYPE=5
},
default: {
showRange: [0, 1000],
imageUrl: `data:image/svg+xml,${encodeURIComponent(`<svg t="1754726456255" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15529" width="200" height="200"><path d="M512 921.6a409.6 409.6 0 1 0 0-819.2 409.6 409.6 0 0 0 0 819.2z m0 102.4C229.2224 1024 0 794.7776 0 512S229.2224 0 512 0s512 229.2224 512 512-229.2224 512-512 512z m0-366.0288L353.792 739.328l30.208-172.3392L256 444.9792 432.896 419.84 512 263.0656 591.104 419.84 768 444.928l-128 122.0608 30.208 172.3392L512 657.9712z" fill="#666666" p-id="15530"></path></svg>`)}`, // 默认图标
},
};
// 使用Cesium的GeoJsonDataSource加载数据
const dataSource = await Cesium.GeoJsonDataSource.load(response, {
stroke: Cesium.Color.HOTPINK,
fill: Cesium.Color.PINK.withAlpha(0.5),
strokeWidth: 3,
markerSymbol: "?",
markerColor: Cesium.Color.YELLOW,
markerSize: 48,
clampToGround: true,
});
// 添加数据源到viewer
await viewer.value.dataSources.add(dataSource);
// 保存数据源引用以便后续控制显示隐藏
aggregationDataSource = dataSource;
// 根据store中的初始状态设置聚合点显示状态
dataSource.show = screenStore.aggregationControl;
// 启用聚合功能
dataSource.clustering.enabled = false; // 启用聚合功能
dataSource.clustering.pixelRange = 20; // 聚合范围(像素)
dataSource.clustering.minimumClusterSize = 3; // 最小聚合数量
// 自定义聚合点样式
dataSource.clustering.clusterBillboards = false;
dataSource.clustering.clusterLabels = false;
dataSource.clustering.clusterPoints = false;
// 创建现代化聚合点样式
const createClusterIcon = (
text: string,
color: string,
size: number = 48
) => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d")!;
canvas.width = size;
canvas.height = size;
// 绘制外圆阴影
ctx.shadowColor = "rgba(0,0,0,0.3)";
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
// 绘制主圆
const gradient = ctx.createRadialGradient(
size / 2,
size / 2,
0,
size / 2,
size / 2,
size / 2
);
gradient.addColorStop(0, color);
gradient.addColorStop(1, adjustBrightness(color, -30));
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(size / 2, size / 2, size / 2 - 4, 0, 2 * Math.PI);
ctx.fill();
// 绘制内圆
ctx.shadowColor = "transparent";
ctx.fillStyle = "rgba(255,255,255,0.9)";
ctx.beginPath();
ctx.arc(size / 2, size / 2, size / 2 - 8, 0, 2 * Math.PI);
ctx.fill();
// 绘制文字
ctx.fillStyle = color;
ctx.font = `bold ${size / 3}px Arial, sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(text, size / 2, size / 2);
return canvas.toDataURL();
};
// 调整颜色亮度的辅助函数
const adjustBrightness = (color: string, amount: number) => {
const hex = color.replace("#", "");
const r = Math.max(
0,
Math.min(255, parseInt(hex.substr(0, 2), 16) + amount)
);
const g = Math.max(
0,
Math.min(255, parseInt(hex.substr(2, 2), 16) + amount)
);
const b = Math.max(
0,
Math.min(255, parseInt(hex.substr(4, 2), 16) + amount)
);
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
};
// 创建不同级别的聚合图标
const pin50 = createClusterIcon("50+", "#e74c3c", 56); // 红色,最大
const pin40 = createClusterIcon("40+", "#f39c12", 52); // 橙色
const pin30 = createClusterIcon("30+", "#f1c40f", 48); // 黄色
const pin20 = createClusterIcon("20+", "#27ae60", 44); // 绿色
const pin10 = createClusterIcon("10+", "#3498db", 40); // 蓝色
// 根据聚合点数量设置不同样式
dataSource.clustering.clusterEvent.addEventListener(
(clusteredEntities: Cesium.Entity[], cluster: any) => {
cluster.label.show = false;
cluster.billboard.show = true;
cluster.billboard.id = cluster.label.id;
cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
if (clusteredEntities.length >= 50) {
cluster.billboard.image = pin50;
} else if (clusteredEntities.length >= 40) {
cluster.billboard.image = pin40;
} else if (clusteredEntities.length >= 30) {
cluster.billboard.image = pin30;
} else if (clusteredEntities.length >= 20) {
cluster.billboard.image = pin20;
} else if (clusteredEntities.length >= 10) {
cluster.billboard.image = pin10;
} else {
// 小于10个点的聚合使用紫色样式
cluster.billboard.image = createClusterIcon(
clusteredEntities.length.toString(),
"#9b59b6",
36
);
}
}
);
// 设置单个点的样式
const entities = dataSource.entities.values;
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
// 获取POI类型
const poiType = entity.properties?.TYPE?.getValue() || "default";
const rule =
displayRules[poiType as keyof typeof displayRules] ||
displayRules.default;
if (entity.billboard) {
// 根据TYPE类型使用对应的图标
entity.billboard.image = new Cesium.ConstantProperty(rule.imageUrl);
entity.billboard.verticalOrigin = new Cesium.ConstantProperty(
Cesium.VerticalOrigin.TOP
);
if (poiType === 5) {
// return;
console.log(entity.billboard, "123123");
// return;
}
// 根据类型设置不同的尺寸
const iconSize =
poiType === 5 || poiType === 1
? 38
: poiType === 2
? 32
: poiType === 3 || poiType === 4
? 28
: 32;
// 创建浮动动画(上下轻微浮动)
const floatAnimation = new Cesium.CallbackProperty(() => {
const time = Date.now() / 1000;
const offset = i * 0.6;
// 使用余弦函数创造平滑的浮动效果
const float = Math.cos(time * 0.95 + offset);
return new Cesium.Cartesian2(0, -3 * float); // 轻微的上下浮动
}, false);
// 移除透明度动画,使用固定颜色
// 设置固定图标大小
entity.billboard.width = new Cesium.ConstantProperty(iconSize);
entity.billboard.height = new Cesium.ConstantProperty(iconSize);
// 应用浮动动画偏移
// entity.billboard.pixelOffset = floatAnimation;
// 设置固定的白色
entity.billboard.color = new Cesium.ConstantProperty(
Cesium.Color.WHITE
);
entity.billboard.heightReference = new Cesium.ConstantProperty(
Cesium.HeightReference.CLAMP_TO_GROUND
);
// 设置距离显示条件
entity.billboard.distanceDisplayCondition = new Cesium.ConstantProperty(
new Cesium.DistanceDisplayCondition(
rule.showRange[0],
rule.showRange[1] - 1500
)
);
entity.billboard.disableDepthTestDistance = new Cesium.ConstantProperty(
Number.POSITIVE_INFINITY
);
entity.billboard.scale = new Cesium.ConstantProperty(0.7);
// // 禁用深度测试,确保图标始终可见
// entity.billboard.disableDepthTestDistance = new Cesium.ConstantProperty(
// Number.POSITIVE_INFINITY
// );
}
// 添加标签显示POI名称,优化文字可读性和垂直居中
if (entity.properties && entity.properties.NAME) {
// 根据类型设置不同的标签显示距离
const labelShowRange = rule.showRange;
// 移除标签缩放动画,使用固定大小
// 创建标签浮动偏移(与billboard同步浮动)
const labelOffset = new Cesium.CallbackProperty(() => {
const time = Date.now() / 1000;
const offset = i * 0.6;
const float = Math.cos(time * 0.8 + offset);
const floatY = -35 - 8 * float; // 增加Y轴偏移,确保标签不被遮挡
return new Cesium.Cartesian2(0, floatY);
}, false);
// 移除标签透明度动画,使用固定颜色
entity.label = new Cesium.LabelGraphics({
text: entity.properties.NAME.getValue(),
font: new Cesium.ConstantProperty(
"16px Microsoft YaHei, Arial, sans-serif"
),
fillColor: new Cesium.ConstantProperty(Cesium.Color.BLACK),
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: new Cesium.ConstantProperty(Cesium.LabelStyle.FILL),
verticalOrigin: new Cesium.ConstantProperty(
Cesium.VerticalOrigin.TOP
),
horizontalOrigin: new Cesium.ConstantProperty(
Cesium.HorizontalOrigin.CENTER
),
// pixelOffset: labelOffset, // 使用浮动动画偏移
pixelOffset: new Cesium.Cartesian2(0, -30),
distanceDisplayCondition: new Cesium.ConstantProperty(
new Cesium.DistanceDisplayCondition(
labelShowRange[0],
labelShowRange[1] - 1500
)
), // 标签显示距离
disableDepthTestDistance: Number.POSITIVE_INFINITY,
show: new Cesium.ConstantProperty(true),
scale: new Cesium.ConstantProperty(0.9), // 使用固定大小
// 添加背景样式增强可读性
showBackground: true,
backgroundColor: new Cesium.ConstantProperty(
Cesium.Color.WHITE.withAlpha(1)
),
backgroundPadding: new Cesium.ConstantProperty(
new Cesium.Cartesian2(8, 4)
),
});
}
}
console.log(`成功加载 ${entities.length} 个POI点,已启用聚合功能`);
} catch (error) {
console.error("加载POI数据失败:", error);
}
};