| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- import * as KMap from "@/utils/ol-map/KMap";
- import { Vector as VectorSource, Cluster } from "ol/source.js";
- import { Feature } from "ol";
- import { WKT } from "ol/format";
- import { Circle, Fill, Stroke, Style, Text, Icon, RegularShape } from "ol/style.js";
- import Photo from "ol-ext/style/Photo";
- import { newPoint, newPolymerFeature } from "@/utils/map.js";
- import pointBgImg from "@/assets/images/map/point_bg.png";
- class DistributionLayer {
- constructor(kmap) {
- let that = this;
- this.kmap = kmap
- let vectorStyle = new KMap.VectorStyle()
- this.distributionLayer = new KMap.VectorLayer("distributionLayer", 99, {
- source: new VectorSource({}),
- style: function (f) {
- let style2 = vectorStyle.getPolygonStyle(f.get("color")+'4d', f.get("color"), 2)
- return [style2]
- }
- });
- this.kmap.addLayer(this.distributionLayer.layer)
- // 创建聚合数据源
- this.clusterSource = new Cluster({
- distance: 80, // 聚合距离(像素)
- minDistance: 50, // 最小聚合距离
- });
- // 提取单个点样式函数
- const getPointStyle = (feature, isCluster = false, clusterSize = 1) => {
- const label = feature.get("label") || "";
- const baseColor = feature.get("color") || "#2199F8";
- const farmName = feature.get("farmName") || "";
- const imgName = feature.get("imgName") || "";
- const speciesIcon = feature.get("speciesIcon");
- // 方块填充色背景(在白色描边 PNG 底图下方,参考 UI)
- const squareBgStyle = new Style({
- image: new RegularShape({
- points: 4,
- radius: 34, // 控制方块大小
- angle: Math.PI / 4, // 旋转 45°,让正方形对齐
- fill: new Fill({
- color: baseColor,
- }),
- imgSize: [34, 34],
- // 向上平移一点,让方块主要位于指针上方
- displacement: [0, 4],
- }),
- });
- const iconAndTextStyle = new Style({
- image: new Icon({
- src: pointBgImg,
- scale: 0.45,
- }),
- // 文字 + 背景(参考 UI:白色圆角矩形 + 彩色文字)
- text: new Text({
- text: label,
- font: "normal 14px sans-serif",
- offsetY: -46, // 文字整体上移,避免和图片重叠
- textAlign: "center",
- fill: new Fill({
- color: baseColor,
- }),
- backgroundFill: new Fill({
- color: "rgba(255,255,255,1)",
- }),
- backgroundStroke: new Stroke({
- color: baseColor,
- width: 1,
- }),
- padding: [2, 14, 2, 14],
- }),
- });
- // 聚合点数量徽章(红色圆形,白色文字,图片右上角)
- // 图片 scale 0.45,假设原始图片约 100x100,实际显示约 45x45
- // 右上角位置:offsetX 约 22-25,offsetY 约 -22-25(相对于图片中心)
- const badgeStyle = isCluster && clusterSize > 1
- ? new Style({
- image: new Circle({
- radius: 10,
- fill: new Fill({
- color: "#FF0000", // 红色
- }),
- // 定位到图片右上角
- displacement: [22, 25], // 相对于图片中心,向右上偏移
- }),
- text: new Text({
- text: clusterSize.toString(),
- font: "bold 11px sans-serif",
- fill: new Fill({
- color: "#FFFFFF", // 白色文字
- }),
- // 文字位置与圆形位置一致
- offsetX: 22,
- offsetY: -25,
- textAlign: "center",
- textBaseline: "middle",
- }),
- })
- : null;
- // 农场名称(显示在点位下方,白色文字)
- // 聚合点显示第一个点的农场名称
- const farmNameStyle = farmName
- ? new Style({
- text: new Text({
- text: farmName,
- font: "normal 18px sans-serif",
- offsetY: 45, // 向下偏移,位于点位下方
- textAlign: "center",
- fill: new Fill({
- color: "#FFFFFF",
- }),
- stroke: new Stroke({
- color: "rgba(0,0,0,0.6)",
- width: 2,
- }),
- }),
- })
- : null;
- const typeImgStyle = new Style({
- image: new Icon({
- // src: imgUrl,
- src: speciesIcon,
- scale: 0.8,
- displacement: [0, 4],
- }),
- });
- // 先画纯色方块,再画白色描边 PNG 和文字,然后画徽章,最后画农场名称
- const styles = [squareBgStyle, iconAndTextStyle];
- if (speciesIcon) styles.push(typeImgStyle);
- if (badgeStyle) styles.push(badgeStyle);
- if (farmNameStyle) styles.push(farmNameStyle);
- return styles;
- };
- this.distributionPointLayer = new KMap.VectorLayer("distributionPointLayer", 99, {
- source: this.clusterSource,
- style: (f) => {
- // 判断是否为聚合点
- const features = f.get('features');
- if (features && features.length > 1) {
- // 聚合点:使用第一个点的样式,但添加数量标识
- const firstFeature = features[0];
- return getPointStyle(firstFeature, true, features.length);
- }
- // 单个点样式(原有逻辑)
- const singleFeature = features && features.length === 1 ? features[0] : f;
- return getPointStyle(singleFeature, false, 1);
- },
- });
- this.kmap.addLayer(this.distributionPointLayer.layer)
- // 设施图层:只显示图标(冷链冷库 / 加工厂等),不带边框和文字
- this.facilityPointLayer = new KMap.VectorLayer("facilityPointLayer", 100, {
- source: new VectorSource({}),
- style: function (f) {
- const imgName = f.get("imgName") || "";
- return new Style({
- image: new Icon({
- src: imgName,
- imgSize: [45, 45],
- width: 45,
- height: 45,
- scale: 0.9,
- }),
- });
- },
- });
- this.kmap.addLayer(this.facilityPointLayer.layer)
- }
- /**
- * 清空当前图层上的所有数据
- */
- clear() {
- if (this.distributionLayer && this.distributionLayer.source) {
- this.distributionLayer.source.clear();
- }
- if (this.clusterSource && this.clusterSource.source) {
- this.clusterSource.source.clear();
- }
- if (this.facilityPointLayer && this.facilityPointLayer.source) {
- this.facilityPointLayer.source.clear();
- }
- }
- initData(data, field = 'speciesName') {
- // 每次加载前先清空旧数据(多用于"作物分布 / 物候期分布 / 农场分布")
- this.clear();
- if(!data || data.length === 0) return;
- // 创建临时 VectorSource 用于存储点数据
- const pointSource = new VectorSource({});
- for (let item of data) {
- // 面数据(区域多边形)
- if (item.geom) {
- item.color = item.speciesColor || "#2199F8";
- this.distributionLayer.source.addFeature(newPolymerFeature(item));
- }
- if (item.centerPoint) {
- item.color = item.speciesColor || "#2199F8";
- item.wkt = item.centerPoint;
- item.label = item[field] || "";
- pointSource.addFeature(newPoint(item));
- }
- }
- // 将点数据源设置到聚合源
- if (pointSource.getFeatures().length > 0) {
- this.clusterSource.setSource(pointSource);
- }
- // const extent = this.distributionLayer.source.getExtent();
- // if (extent && !isNaN(extent[0])) {
- // this.kmap.map.getView().fit(extent, {
- // padding: [280, 400, 200, 150],
- // duration: 500,
- // });
- // }
- // 所有点位添加完成后,地图范围自适应到包含所有点
- if (pointSource.getFeatures().length > 0) {
- setTimeout(() => {
- const pointExtent = this.clusterSource.source.getExtent();
- if (pointExtent && !isNaN(pointExtent[0])) {
- this.kmap.map.getView().fit(pointExtent, {
- padding: [280, 400, 200, 150],
- duration: 500,
- maxZoom: 12,
- });
- }
- }, 100);
- }
- }
- /**
- * 设施数据加载(冷链冷库 / 加工厂)
- * 只操作 facilityPointLayer,不影响作物分布图层
- */
- initFacilityData(list = []) {
- if (!this.facilityPointLayer || !this.facilityPointLayer.source) return;
- this.facilityPointLayer.source.clear();
- const extentSource = new VectorSource({});
- for (let item of list) {
- if (!item.wktArr) continue;
- for (let wkt of item.wktArr) {
- const f = new Feature({
- geometry: new WKT().readGeometry(wkt),
- });
- f.set("imgName", item.imgName);
- this.facilityPointLayer.source.addFeature(f);
- extentSource.addFeature(f);
- }
- }
- const extent = extentSource.getExtent();
- if (extent && !isNaN(extent[0])) {
- this.kmap.map.getView().fit(extent, {
- padding: [280, 400, 200, 150],
- duration: 500,
- });
- }
- }
- }
- export default DistributionLayer;
|