|
@@ -0,0 +1,414 @@
|
|
|
+import Style from "ol/style/Style";
|
|
|
+import Photo from "ol-ext/style/Photo";
|
|
|
+import { newPolymerFeature, newPoint } from "@/common/util";
|
|
|
+import Icon from "ol/style/Icon";
|
|
|
+import { Cluster, Vector as VectorSource } from "ol/source.js";
|
|
|
+import { Vector } from "ol/layer.js";
|
|
|
+import * as KMap from '@/utils/ol-map/KMap';
|
|
|
+import { Fill, Text, Circle, Stroke } from "ol/style.js";
|
|
|
+import { boundingExtent } from 'ol/extent.js';
|
|
|
+import { toLonLat } from 'ol/proj';
|
|
|
+import CircleStyle from 'ol/style/Circle.js';
|
|
|
+import {base_img_url2} from "@/api/config.js"
|
|
|
+import eventBus from "@/api/eventBus";
|
|
|
+import router from '@/router'
|
|
|
+/**
|
|
|
+ * 行政区县级天气点位数据
|
|
|
+ */
|
|
|
+class MockFarmLayer {
|
|
|
+ constructor(kmap) {
|
|
|
+ let that = this
|
|
|
+ this.nameStyleCache = {}
|
|
|
+ this.cloudFilenameCache = {}
|
|
|
+ this.statusTitleStyleCache = {}
|
|
|
+ this.textBgStyleCache = {}
|
|
|
+ this.bgStyleCache = {}
|
|
|
+ this.typeInfoStyleCache = {}
|
|
|
+ this.pointStyle = new Style({
|
|
|
+ renderer: function (coordinates, state) {
|
|
|
+ let ctx = state.context;
|
|
|
+ ctx.strokeStyle = 'black'; // 边框颜色
|
|
|
+ ctx.lineWidth = 2; // 边框宽度
|
|
|
+ ctx.fillStyle = 'rgba(29,28,28,0.8)'; // 填充颜色
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.ellipse(coordinates[0], coordinates[1], 7 * state.pixelRatio, 2 * state.pixelRatio, 0, 0, 2 * Math.PI);// 绘制椭圆
|
|
|
+ ctx.fill();// 填充椭圆
|
|
|
+ ctx.stroke();// 绘制椭圆边框
|
|
|
+ },
|
|
|
+ zIndex:-1
|
|
|
+ });
|
|
|
+ that.initLayer(kmap)
|
|
|
+
|
|
|
+ // 点击点位
|
|
|
+ this.currentPoint = null
|
|
|
+ // eventBus.on("MockFarmLayer:click", function ({event, feature}) {
|
|
|
+ // if (that.currentPoint && that.currentPoint.get("id") != "10117") {
|
|
|
+ // that.currentPoint.set("selected", false)
|
|
|
+ // }
|
|
|
+ // // that.currentPoint = feature
|
|
|
+ // const f = that.selectFeature(feature)
|
|
|
+ // that.currentPoint = f
|
|
|
+ // f.set("selected", true)
|
|
|
+ // })
|
|
|
+
|
|
|
+ // eventBus.on("MockFarmLayer:resetPoint", function () {
|
|
|
+ // if (that.currentPoint && that.currentPoint.get("id") != "10117") {
|
|
|
+ // that.currentPoint.set("selected", false)
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*************************************************************************************************
|
|
|
+ 图层初始化的函数
|
|
|
+ *************************************************************************************************/
|
|
|
+ initLayer(kmap){
|
|
|
+ let that = this;
|
|
|
+ this.vectorStyle = new KMap.VectorStyle()
|
|
|
+ this.clusterSource = new VectorSource({})
|
|
|
+ this.clusterLayer = new Vector({
|
|
|
+ source: new Cluster({
|
|
|
+ distance: 80,
|
|
|
+ source: this.clusterSource,
|
|
|
+ }),
|
|
|
+ name: "MockFarmLayer",
|
|
|
+ minZoom: 9,
|
|
|
+ maxZoom: 15,
|
|
|
+ zIndex: 1001,
|
|
|
+ style: function (feature) {
|
|
|
+ let f = that.selectFeature(feature)
|
|
|
+ const isCurrent = f.get("selected");
|
|
|
+ const isHigh = f.get("isHigh") || f.get("id") == 10117 || f.get("isDemo");
|
|
|
+ let imgType = f.get("pz") ? f.get("pz").slice(0, 2) : "荔枝";
|
|
|
+ console.log('imgType', imgType);
|
|
|
+ let img = f.get("id")==10117 ? require("@/assets/images/map/logo.jpg") : `https://birdseye-img.sysuimars.com/temp/pz/${imgType}.png`;
|
|
|
+ // let name = f.get("farmName");
|
|
|
+ let name = f.get("yield");
|
|
|
+ let id = f.get("mapId");
|
|
|
+ let typeImg = require("@/assets/images/foster-home/tree-item.png");
|
|
|
+ // let styles = [that.cloudStyle(img, isCurrent), that.pointBgStyle(name, isCurrent), that.nameStyle(id, name, isCurrent)];
|
|
|
+ let styles = [that.cloudStyle(img, isCurrent, isHigh), that.pointBgStyle(name, isCurrent, isHigh)];
|
|
|
+ that.setProperties(feature,f)
|
|
|
+ // let text = f.get("pz");
|
|
|
+ let text = f.get("pz") ? that.firstVariety(f.get("pz")) : "荔枝";
|
|
|
+ let fontColor = (isCurrent || isHigh) ? "#fff" : "#000"
|
|
|
+ const yieldVal = f.get("yield") ? (f.get("yield")+"吨") : "--"
|
|
|
+ const price = "7.5"
|
|
|
+ // if(name==='HS.89215'){
|
|
|
+ // name = ''
|
|
|
+ // }
|
|
|
+
|
|
|
+ // const startColor = isCurrent ? "#140D00" : "#00000099"
|
|
|
+ // const endColor = isCurrent ? "#7A4E00" : "#00000099"
|
|
|
+ const startColor = (isCurrent || isHigh) ? "#140D00" : "#FFD489"
|
|
|
+ const endColor = (isCurrent || isHigh) ? "#7A4E00" : "#FFD489"
|
|
|
+
|
|
|
+ // styles.push(that.typeInfoStyle(typeImg, "", "15", isCurrent));
|
|
|
+ const textOffsetY = (isCurrent || isHigh) ? -120 : -100
|
|
|
+ // styles.push(that.statusTitleStyle(text, -16, textOffsetY, fontColor,"#ffffff00", 14));
|
|
|
+ // styles.push(that.statusTitleStyle(text, -16, textOffsetY, fontColor,"#ffffff00", 14));
|
|
|
+ // styles.push(that.statusTitleStyle(price, 28, textOffsetY, "#FFD489","#ffffff00", 18));
|
|
|
+ // styles.push(that.statusTitleStyle("元/斤", 56, textOffsetY, "#FFFFFF66","#ffffff00", 10));
|
|
|
+
|
|
|
+ styles.push(that.textBgStyle(startColor, endColor, yieldVal+text+"123", isCurrent, isHigh));
|
|
|
+ styles.push(that.statusTitleStyle(text + " " + yieldVal, 0, textOffsetY, fontColor,"#ffffff00", 14));
|
|
|
+ // const zoom = kmap.getView().getZoom();
|
|
|
+ // if (zoom > 9) {
|
|
|
+ // }
|
|
|
+ return styles;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ kmap.addLayer(this.clusterLayer);
|
|
|
+
|
|
|
+ // kmap.getView().on('change:resolution', function() {
|
|
|
+ // const zoom = kmap.getView().getZoom();
|
|
|
+ // const clusterSource = that.clusterLayer.getSource();
|
|
|
+ // console.log('zoom', zoom);
|
|
|
+ // if (zoom < 9) {
|
|
|
+ // clusterSource.setDistance(10); // 缩放级别小于7时,聚合距离为10
|
|
|
+ // } else {
|
|
|
+ // clusterSource.setDistance(80); // 缩放级别大于等于7时,聚合距离为80
|
|
|
+ // }
|
|
|
+ // }.bind(this));
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ firstVariety(pz) {
|
|
|
+ const match = pz.match(/[^、,。\s]+/);
|
|
|
+ return match ? match[0] : '';
|
|
|
+ }
|
|
|
+ /*************************************************************************************************
|
|
|
+ 加载数据相关的函数
|
|
|
+ *************************************************************************************************/
|
|
|
+ setData(data){
|
|
|
+ this.clusterSource.clear()
|
|
|
+ let features = []
|
|
|
+ for(let item of data){
|
|
|
+ // if(item.name==='HS.89215'){
|
|
|
+ // item.name = ''
|
|
|
+ // item.wkt = 'POINT(113.5081595 23.5320866)'
|
|
|
+ // console.log('GZ.88209',item);
|
|
|
+ // }
|
|
|
+ if (item.geom) {
|
|
|
+ item.wkt = item.geom
|
|
|
+ try {
|
|
|
+ features.push(newPoint(item,"wkt","mock_farm_data"))
|
|
|
+ } catch (err) {
|
|
|
+ console.log('err', err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.clusterSource.addFeatures(features)
|
|
|
+ }
|
|
|
+ /*************************************************************************************************
|
|
|
+ 样式相关的函数
|
|
|
+ *************************************************************************************************/
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param startColor '#ffc91a'
|
|
|
+ * @param endColor '#d2a106'
|
|
|
+ * @returns {Style}
|
|
|
+ */
|
|
|
+ textBgStyle(startColor, endColor, text = "", isCurrent, isHigh) {
|
|
|
+ let key = startColor + endColor + text + isCurrent + isHigh; // 缓存键加入文字内容
|
|
|
+ let style = this.textBgStyleCache[key];
|
|
|
+ if (!style) {
|
|
|
+ style = new Style({
|
|
|
+ renderer: function (coordinates, state) {
|
|
|
+ let ctx = state.context;
|
|
|
+
|
|
|
+ // 1. 先测量文字宽度
|
|
|
+ ctx.font = "14px Arial"; // 设置字体样式(需与最终显示文字的样式一致)
|
|
|
+ const textWidth = ctx.measureText(text).width;
|
|
|
+
|
|
|
+ // 2. 动态计算矩形宽度(基础宽度 + 文字宽度)
|
|
|
+ const padding = 16 * state.pixelRatio; // 左右内边距
|
|
|
+ const dynamicWidth = textWidth + padding * 2 + 26; // 总宽度 = 文字宽度 + 内边距
|
|
|
+
|
|
|
+ // 3. 矩形参数(高度和圆角不变)
|
|
|
+ const x = coordinates[0];
|
|
|
+ const y = (isCurrent||isHigh) ? (coordinates[1] - 120 * state.pixelRatio) : (coordinates[1] - 100 * state.pixelRatio);
|
|
|
+ const height = 40 * state.pixelRatio;
|
|
|
+ const cornerRadius = 20 * state.pixelRatio;
|
|
|
+
|
|
|
+ // 4. 创建渐变(根据动态宽度调整)
|
|
|
+ const gradient = ctx.createLinearGradient(
|
|
|
+ x - dynamicWidth / 2, y,
|
|
|
+ x + dynamicWidth / 2, y
|
|
|
+ );
|
|
|
+ gradient.addColorStop(0, startColor);
|
|
|
+ gradient.addColorStop(1, endColor);
|
|
|
+
|
|
|
+ // 5. 绘制圆角矩形(宽度用 dynamicWidth)
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x - dynamicWidth / 2 + cornerRadius, y - height / 2);
|
|
|
+ ctx.lineTo(x + dynamicWidth / 2 - cornerRadius, y - height / 2);
|
|
|
+ ctx.arc(x + dynamicWidth / 2 - cornerRadius, y - height / 2 + cornerRadius,
|
|
|
+ cornerRadius, -Math.PI / 2, 0);
|
|
|
+ ctx.lineTo(x + dynamicWidth / 2, y + height / 2 - cornerRadius);
|
|
|
+ ctx.arc(x + dynamicWidth / 2 - cornerRadius, y + height / 2 - cornerRadius,
|
|
|
+ cornerRadius, 0, Math.PI / 2);
|
|
|
+ ctx.lineTo(x - dynamicWidth / 2 + cornerRadius, y + height / 2);
|
|
|
+ ctx.arc(x - dynamicWidth / 2 + cornerRadius, y + height / 2 - cornerRadius,
|
|
|
+ cornerRadius, Math.PI / 2, Math.PI);
|
|
|
+ ctx.lineTo(x - dynamicWidth / 2, y - height / 2 + cornerRadius);
|
|
|
+ ctx.arc(x - dynamicWidth / 2 + cornerRadius, y - height / 2 + cornerRadius,
|
|
|
+ cornerRadius, Math.PI, -Math.PI / 2);
|
|
|
+ ctx.closePath();
|
|
|
+ ctx.fillStyle = gradient;
|
|
|
+ ctx.fill();
|
|
|
+ },
|
|
|
+ zIndex: 2
|
|
|
+ });
|
|
|
+ this.textBgStyleCache[key] = style;
|
|
|
+ }
|
|
|
+ return style;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ statusTitleStyle(statusName, offsetX, offsetY,color,strokeColor, fontSize){
|
|
|
+ const key = statusName + "-" + offsetY
|
|
|
+ let style = this.statusTitleStyleCache[key]
|
|
|
+ if (!style) {
|
|
|
+ style = new Style({
|
|
|
+ text: new Text({
|
|
|
+ text: statusName,
|
|
|
+ offsetX: offsetX,
|
|
|
+ offsetY: offsetY,
|
|
|
+ font: `bold ${fontSize}px sans-serif`,
|
|
|
+ fill: new Fill({ color }), // 字体颜色
|
|
|
+ stroke: new Stroke({ color: strokeColor }), // 字体颜色
|
|
|
+ }),
|
|
|
+ zIndex:3
|
|
|
+ });
|
|
|
+ this.statusTitleStyleCache[key] = style
|
|
|
+ }
|
|
|
+ return style
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 农场名称样式
|
|
|
+ * @param id
|
|
|
+ * @param name
|
|
|
+ * @returns {Style}
|
|
|
+ */
|
|
|
+ nameStyle(id, name, isCurrent, isHigh) {
|
|
|
+ const key = name + isCurrent + isHigh
|
|
|
+ let nameStyle = this.nameStyleCache[key]
|
|
|
+ if (!nameStyle) {
|
|
|
+ nameStyle = new Style({
|
|
|
+ text: new Text({
|
|
|
+ text: name,
|
|
|
+ offsetX: 0,
|
|
|
+ offsetY: 8,
|
|
|
+ font: `bold ${isCurrent||isHigh ? 22 : 16}px sans-serif`,
|
|
|
+ fill: new Fill({ color: isCurrent||isHigh ? "#CB8713" : "#1D1D1D" }), // 字体颜色
|
|
|
+ stroke: new Stroke({ width: 1.3,color: "#fff" }), // 字体颜色
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ this.nameStyleCache[key] = nameStyle
|
|
|
+ }
|
|
|
+ return nameStyle
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 农场照片样式
|
|
|
+ * @param cloudFilename
|
|
|
+ * @returns {Style}
|
|
|
+ */
|
|
|
+ cloudStyle(cloudFilename, isCurrent, isHigh){
|
|
|
+ let that= this
|
|
|
+ const key = cloudFilename + isCurrent + isHigh
|
|
|
+ let cloudStyle = this.cloudFilenameCache[key]
|
|
|
+ if (!cloudStyle) {
|
|
|
+ let cloudUrl = `${cloudFilename}?imageView2/1/w/100`
|
|
|
+ cloudStyle = new Style({
|
|
|
+ image: new Photo({
|
|
|
+ src: cloudUrl,
|
|
|
+ radius: isHigh ? 39 : (isCurrent ? 36 : 30),
|
|
|
+ crop: true,
|
|
|
+ displacement: [0, isCurrent||isHigh ? 58 : 42],
|
|
|
+ stroke: new Stroke({
|
|
|
+ width: 0,
|
|
|
+ color: "#fdfcfc00",
|
|
|
+ }),
|
|
|
+ onload: function () {
|
|
|
+ that.clusterLayer &&
|
|
|
+ that.clusterLayer.changed();
|
|
|
+ },
|
|
|
+ }),
|
|
|
+ zIndex: isHigh ? 10 : 0
|
|
|
+ })
|
|
|
+ this.cloudFilenameCache[key] = cloudStyle
|
|
|
+ }
|
|
|
+ return cloudStyle
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 点位背景样式
|
|
|
+ * @returns {Style}
|
|
|
+ */
|
|
|
+ pointBgStyle(name, isCurrent, isHigh){
|
|
|
+ const key = name + isCurrent + isHigh
|
|
|
+ let bgStyle = this.bgStyleCache[key]
|
|
|
+ if (!bgStyle) {
|
|
|
+ bgStyle = new Style({
|
|
|
+ image: new Photo({
|
|
|
+ src: isCurrent||isHigh ? require("@/assets/images/map/point-bg-active.png") : require("@/assets/images/map/point-bg.png"),
|
|
|
+ radius: isHigh ? 55 : (isCurrent ? 50 : 36),
|
|
|
+ shadow: 0,
|
|
|
+ crop: false,
|
|
|
+ displacement: [0, isCurrent||isHigh ? 52 : 38],
|
|
|
+ stroke: new Stroke({
|
|
|
+ width: 0,
|
|
|
+ color: "#fdfcfc00",
|
|
|
+ }),
|
|
|
+ }),
|
|
|
+ zIndex: isHigh ? 11 : 0
|
|
|
+ });
|
|
|
+ this.bgStyleCache[key] = bgStyle
|
|
|
+ }
|
|
|
+ return bgStyle
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 品种图片加文字样式
|
|
|
+ * @returns {Style}
|
|
|
+ */
|
|
|
+ typeInfoStyle(img, name, price, isCurrent, isHigh){
|
|
|
+ let key = img + name + price + isCurrent+isHigh; // 缓存键
|
|
|
+ let typeStyle = this.typeInfoStyleCache[key]
|
|
|
+ if (!typeStyle) {
|
|
|
+ // 1. 创建一个临时的 Canvas 上下文用于测量文字宽度
|
|
|
+ const canvas = document.createElement("canvas");
|
|
|
+ const ctx = canvas.getContext("2d");
|
|
|
+
|
|
|
+ // 2. 设置字体样式(需与最终显示文字的样式一致)
|
|
|
+ ctx.font = "14px Arial"; // 假设文字样式为 14px Arial
|
|
|
+
|
|
|
+ // 3. 计算 name 和 price 的总宽度(包括间距)
|
|
|
+ const nameWidth = ctx.measureText(name).width;
|
|
|
+ const priceWidth = ctx.measureText(price).width;
|
|
|
+ const totalTextWidth = nameWidth + priceWidth + 70; // 10px 作为间距
|
|
|
+
|
|
|
+ // 4. 动态计算图片的横向偏移量(使其位于最左侧)
|
|
|
+ const imageRadius = 14; // 图片半径(与 Photo 的 radius 一致)
|
|
|
+ const offsetX = - (totalTextWidth / 2 + imageRadius + 5); // 5px 额外间距
|
|
|
+ typeStyle = new Style({
|
|
|
+ image: new Photo({
|
|
|
+ src: img,
|
|
|
+ kind: "circle",
|
|
|
+ radius: 14,
|
|
|
+ shadow: 0,
|
|
|
+ crop: true,
|
|
|
+ displacement: [offsetX, isCurrent||isHigh ? 124 : 100],
|
|
|
+ stroke: new Stroke({
|
|
|
+ width: 0,
|
|
|
+ color: "#fdfcfc00",
|
|
|
+ }),
|
|
|
+ }),
|
|
|
+ zIndex: 4
|
|
|
+ });
|
|
|
+ this.typeInfoStyleCache[key] = typeStyle
|
|
|
+ }
|
|
|
+ return typeStyle
|
|
|
+ }
|
|
|
+
|
|
|
+ /*************************************************************************************************
|
|
|
+ 其他函数
|
|
|
+ *************************************************************************************************/
|
|
|
+ setProperties(feature,f) {
|
|
|
+ let disease = f.get("disease");
|
|
|
+ let cloudFilename = f.get("cloudFilename");
|
|
|
+ let mockFarmId = f.get("mockFarmId");
|
|
|
+ let id = f.get("mapId");
|
|
|
+ let targetSampleId = f.get("targetSampleId");
|
|
|
+ feature.set("id",id)
|
|
|
+ feature.set("mockFarmId",mockFarmId)
|
|
|
+ feature.set("targetSampleId",targetSampleId)
|
|
|
+ }
|
|
|
+ selectFeature(feature) {
|
|
|
+ let fs = feature.get("features");
|
|
|
+ if (fs.length === 1) {
|
|
|
+ return fs[0];
|
|
|
+ } else {
|
|
|
+ // 优先返回有 isHight 的项
|
|
|
+ for (let item of fs) {
|
|
|
+ if (item.get("isHight") || item.get("id") == 10117 || item.get("isDemo")) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 其次返回有 disease 的项
|
|
|
+ for (let item of fs) {
|
|
|
+ if (item.get("disease")) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 如果都没有,返回第一个项
|
|
|
+ return fs[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+export default MockFarmLayer;
|
|
|
+// new MockFarmLayer()
|