Quellcode durchsuchen

feat:修改首页地图点位样式图片

wangsisi vor 1 Monat
Ursprung
Commit
369e0045ea

+ 6 - 2
package.json

@@ -16,7 +16,7 @@
     "@iconify/iconify": "^3.1.0",
     "@iconify/json": "^2.2.42",
     "@iconify/vue": "^4.1.0",
-    "@turf/turf": "^7.1.0",
+    "@turf/turf": "^7.2.0",
     "@vueuse/core": "^8.7.5",
     "axios": "^0.27.2",
     "core-js": "^3.23.3",
@@ -28,6 +28,9 @@
     "html2canvas": "^1.4.1",
     "jquery": "^3.6.3",
     "jsts": "^2.9.3",
+    "mars3d": "^3.9.1",
+    "mars3d-cesium": "^1.127.0",
+    "mars3d-tdt": "^3.9.1",
     "mitt": "^3.0.1",
     "moment": "^2.29.4",
     "normalize.css": "^8.0.1",
@@ -46,11 +49,11 @@
     "vue-fontawesome": "^0.0.2",
     "vue-router": "^4.1.0",
     "vue3-openlayers": "^0.1.74",
+    "vue3-photo-preview": "^0.3.0",
     "vue3-print-nb": "^0.1.4",
     "vuedraggable": "^2.24.3",
     "vueshowpdf": "^1.1.2",
     "vuex": "^4.0.2",
-    "vue3-photo-preview": "^0.3.0",
     "xe-utils": "^3.5.4",
     "yarn": "^1.22.22",
     "zdog": "^1.1.3"
@@ -67,6 +70,7 @@
     "@vue/compiler-sfc": "^3.2.37",
     "@vue/eslint-config-prettier": "^7.0.0",
     "compression-webpack-plugin": "^10.0.0",
+    "copy-webpack-plugin": "^13.0.0",
     "eslint": "^8.19.0",
     "eslint-plugin-prettier": "^4.2.1",
     "eslint-plugin-vue": "^9.1.1",

+ 1 - 1
src/api/modules/image.js

@@ -10,7 +10,7 @@ module.exports = {
         type: "post",
     },
     pointByRegionId:{
-        url: config.base_dev_url + "image/pointByRegionId",
+        url: config.base_dev_url + "image/pointByRegionId?key="+config.mini_key,
         type: "post",
     },
     findSuitabilityByPoint: {

BIN
src/assets/images/map/status/status-bh.png


BIN
src/assets/images/map/status/status-ch.png


BIN
src/assets/images/map/status/status-szyc.png


BIN
src/assets/images/map/status/status_zc.png


+ 22 - 11
src/utils/util.js

@@ -1,28 +1,39 @@
+import WKT from "ol/format/WKT.js";
+
 export function cleanArrayUndefined(arr) {
     let nr = [];
-    arr.forEach(v => {
-        if(v){
-            nr.push(v)
+    arr.forEach((v) => {
+        if (v) {
+            nr.push(v);
         }
-    })
+    });
     return nr;
 }
 
-export function getValues(arr,key) {
+export function getValues(arr, key) {
     let ids = [];
-    arr.forEach(item => {
+    arr.forEach((item) => {
         ids.push(item[key]);
-    })
+    });
     return ids;
 }
 
-export function getMethodValues(arr,method,key) {
+export function getMethodValues(arr, method, key) {
     let ids = [];
-    arr.forEach(item => {
+    arr.forEach((item) => {
         ids.push(item[method](key));
-    })
+    });
     return ids;
 }
 
+export const pointToFormat = (pointString) => {
+    let geom = new WKT().readGeometry(pointString);
+    const longitude = geom.getCoordinates()[0];
+    const latitude = geom.getCoordinates()[1];
+    // 转换为度的格式
+    const formattedLongitude = `${longitude.toFixed(0)}°E`;
+    const formattedLatitude = `${latitude.toFixed(0)}°N`;
 
-
+    const result = `${formattedLongitude},${formattedLatitude}`;
+    return result;
+};

+ 2 - 2
src/views/home/album_compoents/albumCarousel.vue

@@ -34,8 +34,8 @@ startDate.setMonth(currentDate.getMonth() - 1);
 const formattedStartDate = dateFormat(startDate, 'YY-mm-dd');
 const formattedEndDate = dateFormat(currentDate, 'YY-mm-dd');
 
-eventBus.on("click:point",function({name}){
-  let params = {sampleId: name,farmId: farmId.value, startDate: formattedStartDate, endDate: formattedEndDate}
+eventBus.on("click:point",function({farmId,sampleId}){
+  let params = {sampleId,farmId}
   VE_API.miniimage.list(params).then(res => {
     if(res.code === 0){
       images.value = res.data

+ 1 - 5
src/views/home/album_compoents/albumCarouselItem.vue

@@ -2,7 +2,7 @@
   <div class="carousel-container">
     <!-- 图片列表 -->
     <div class="carousel-wrapper"  :style="carouselStyle">
-      <photo-provider class="provider" v-if="images" :photo-closable="true" @visibleChange="handleVisibleChange">
+      <photo-provider v-if="images" :photo-closable="true" @visibleChange="handleVisibleChange">
         <template  v-for="(photo, index) in images"
                    :key="photo.id">
           <album-draw-box  :farmId="farmId" :photo="photo" :current="currentIndex" :index="index" :length="images.length"
@@ -123,16 +123,12 @@ const clearAndRestartTimer = () => {
 .carousel-container {
   position: relative;
   width: 100%;
-  height: 100%;
   overflow: hidden;
   margin: 0 auto;
   .carousel-wrapper {
     display: flex;
     transition: transform 0.5s ease;
     width: 100%;
-    .provider{
-      display: flex;
-    }
   }
   .blur-bg {
     position: absolute;

+ 85 - 116
src/views/home/album_compoents/albumDrawBox.vue

@@ -1,7 +1,7 @@
 <template>
   <photo-consumer
       class="carousel-item"
-      :src="watermark || base_img_url2 + photo.filename + resize"
+      :src="watermark || base_img_url2 + (photo.resFilename ? photo.resFilename : photo.filename) + resize"
   >
     <img v-if="Math.abs(current - index) < 3" crossorigin="anonymous" @load="drawWatermark($event)" loading="lazy" :src="watermark || (base_img_url2 + (photo.resFilename ? photo.resFilename : photo.filename) + resize)" style="width: 100%;" />
     <canvas  ref="canvasRef" style="position: absolute;"></canvas>
@@ -18,12 +18,15 @@
 <script setup>
 import { ref, onMounted, onBeforeUnmount, defineProps } from "vue";
 import { base_img_url2 } from "@/api/config";
-import {imageCache} from "./cacheImg.js"
+import {imageCache,loadImage} from "./cacheImg.js"
 import {dateFormat} from "@/utils/date_util.js"
-const resize = "?x-oss-process=image/resize,p_80/format,webp/quality,q_40";
+import {pointToFormat} from "@/utils/util.js"
+import {drawTextInRect, drawBorderImageInRect, drawImageInRect, drawRectInRect, drawHorizontalTextList} from "./utils"
+const resize = "?x-oss-process=image/resize,p_30/format,webp/quality,q_40";
 
 const canvasRef = ref(null);
 const watermark = ref(null)
+const baseMapBig = ref(false)
 
 const props = defineProps({
   farmId:{
@@ -48,12 +51,30 @@ const props = defineProps({
   }
 })
 let img = null;
-function drawWatermark(event) {
+let ctx = null;
+let data = {year:props.photo.uploadDate.substring(0,4),
+  monthDay:dateFormat(new Date(props.photo.uploadDate),'mm/dd'),
+  address:props.photo.district.replaceAll("\"","") + props.photo.gardenName,
+  tempImg:imageCache.get("temp"),temp:"10°C-20°C",wendu:"适宜",
+  feiniao:imageCache.get("feiniao"),
+  baseMap:imageCache.get("base_map_"+props.photo.treeId),
+  fusheImg:imageCache.get("fushe"),fushe:"光照优",
+  shiduImg:imageCache.get("shidu"),shidu:"湿度适宜",
+  text:"病害风险,及时喷药",
+  shotCode:props.photo.shotCode,
+  treeCode:props.photo.treeCode,
+  pingzhong:props.photo.pingzhong,
+  uploadDate:props.photo.uploadDate,
+}
+
+async function drawWatermark(event) {
   img = event.target
+  await loadImage(props.photo.baseMap,"base_map_"+props.photo.treeId)
+  data.baseMap = imageCache.get("base_map_"+props.photo.treeId)
   if(!watermark.value){
     let param = {farmId:props.farmId, date: props.photo.uploadDate}
     let weather = null
-    VE_API.miniimage.getByFarmIdAndDate(param).then((res)=>{
+    VE_API.image.findSuitabilityByPoint(param).then((res)=>{
       if(res.code === 0){
         weather = res.data
         drawWatermark2(img,weather)
@@ -69,135 +90,55 @@ function drawWatermark2(img,weather) {
   let scale = 3
   canvas.width = img.width * scale;
   canvas.height = img.height * scale;
-  const ctx = canvas.getContext('2d');
+  ctx = canvas.getContext('2d');
   ctx.scale(scale, scale)
   ctx.drawImage(img, 0, 0, img.width, img.height);
-  drawBottom(ctx, img.width, img.height, weather)
+  drawBottom(img.width, img.height, weather)
   watermark.value = canvas.toDataURL();
 }
 
-let data = {year:props.photo.uploadDate.substring(0,4),
-  monthDay:dateFormat(new Date(props.photo.uploadDate),'mm/dd'),
-  address:props.photo.district.replaceAll("\"","") + props.photo.gardenName,
-  tempImg:imageCache.get("temp"),temp:"20°C",
-  feiniao:imageCache.get("feiniao"),
-  fusheImg:imageCache.get("fushe"),fushe:"光照优",
-  shiduImg:imageCache.get("shidu"),shidu:"湿度适宜",
-  shotCode:props.photo.shotCode
-}
+
 // console.log(data)
-const drawBottom = (ctx, imgWidth, imgHeight, weather) => {
-  if(weather){
-    data["temp"] = weather.tempMin+"°C"
-    let fushe = "光照优"
-    if(weather.uvIndex > 5){
-      fushe = "辐射过高"
-    }else if(weather.uvIndex < 1){
-      fushe = "光照差"
-    }
-    data["fushe"] = fushe
-    let shidu = "湿度适宜"
-    if(weather.humidity < 10){
-      shidu = "干旱缺水"
-    }
-    data["shidu"] = shidu
+const drawBottom = (imgWidth, imgHeight, weather) => {
+  if (weather) {
+    data["temp"] = weather.tempMin + "°C" + "-" + weather.tempMax + "°C" + " " + weather.tempSuitability
+    data["fushe"] = "光照"+weather.vindexSuitability
+    data["shidu"] = "湿度"+weather.humiditySuitability
   }
-  // 设置遮罩的高度为imgHeight的1/6
-  const maskHeight = imgHeight / 4;
-
-  // 绘制黑色半透明遮罩
-  ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; // 黑色,50%透明度
-  ctx.fillRect(0, imgHeight - maskHeight, imgWidth, maskHeight);
-
   // 设置文本样式
-  ctx.fillStyle = "white"; // 文本颜色为白色
   ctx.font = "8px Arial";
   ctx.textAlign = "left"; // 设置为左对齐
+  let imgRect = { x: 0, y: 0, width: imgWidth, height: imgHeight}
+  // 绘制头部黑色半透明遮罩
+  let topRect = drawRectInRect(ctx,imgRect, 0, 0, 70, 10,"rgba(0, 0, 0, 0.6)")
+  // 绘制黑色半透明遮罩
+  let bottomRect = drawRectInRect(ctx,imgRect, 0, 5/6 * 100, 100, 1/6 * 100,"rgba(0, 0, 0, 0.6)")
 
-  // 从遮罩的底部开始绘制文本
-  let currentY = imgHeight - maskHeight + 10; // 字段的起始Y坐标
-  let currentX = 10; // 字段的起始X坐标
-
-  // 绘制年份
-  const field1 = `${data.year}`;
-  ctx.fillText(field1, currentX, currentY+10);
-  currentY += 5;
-  // 计算下划线的起始和结束坐标
-  const underlineY = currentY + 10; // 下划线Y坐标稍微低于文本
-  const underlineWidth = ctx.measureText(field1).width; // 下划线的宽度与文本一致
-  ctx.strokeStyle = "white"; // 下划线颜色为白色
-  ctx.lineWidth = 1; // 下划线的宽度
-  ctx.beginPath();
-  ctx.moveTo(currentX, underlineY); // 下划线起始点
-  ctx.lineTo(currentX + underlineWidth, underlineY); // 下划线结束点
-  ctx.stroke(); // 绘制下划线
-
-  // 绘制日期
-  currentY += 20;
-  currentX += -3
+  drawHorizontalTextList(ctx, topRect, '#ffffff90',[data.treeCode, "蓬径:5m", "高度:3m", "高产树",data.pingzhong],
+      40, 0,
+      65 , 0,"|",0,
+      30,"#ffffff50",1,2)
   ctx.fillStyle = "white"; // 文本颜色为白色
-  ctx.font = "20px Arial";
-  ctx.textAlign = "left"; // 设置为左对齐
-  const field2 = `${data.monthDay}`;
-  ctx.fillText(field2, currentX, currentY + 10);
-
-  // 绘制一个圆点
-  currentX += 60
-  ctx.beginPath();
-  ctx.arc(currentX, imgHeight - maskHeight + (maskHeight / 2 + 1), 2, 0, Math.PI * 2); // 绘制圆形
-  ctx.fill(); // 填充圆形
-
-  // 绘制地址
-  currentY += -10;
-  currentX += 10
-  ctx.font = "8px Arial";
-  const field3 = `${data.address}`;
-  ctx.fillText(field3, currentX, currentY );
-
   // 绘制温度
-  currentY += 8;
-  currentX += -6
-  ctx.drawImage(data.tempImg, currentX + 2, currentY + 1, 13, 13);
-  const field4 = `${data.temp}`;
-  currentX += 15
-  currentY += 12
-  ctx.fillText(field4, currentX, currentY);
-
+  let startXPercent = 1;
+  drawImageInRect(ctx, bottomRect, data.tempImg, startXPercent, 5+10, 5, 30)
+  drawTextInRect(ctx, bottomRect,`${data.temp}`,startXPercent + 4, 28+10, 20)
   // 绘制湿度
-  currentX += 20
-  currentY += -12
-  ctx.drawImage(data.shiduImg, currentX +2, currentY + 1, 13, 13);
-  currentX += 16
-  currentY += 12
-  ctx.font = "8px Arial";
-  const field5 = `${data.shidu}`;
-  ctx.fillText(field5, currentX, currentY);
-
+  drawImageInRect(ctx, bottomRect, data.shiduImg, startXPercent+26, 7 + 10, 4, 30)
+  drawTextInRect(ctx, bottomRect,`${data.shidu}`,startXPercent + 31, 28 + 10, 20)
   // 绘制辐射
-  currentX += 32
-  currentY += -12
-  ctx.drawImage(data.fusheImg, currentX + 2, currentY + 1, 13, 13);
-  currentX += 16
-  currentY += 12
-  ctx.font = "8px Arial";
-  const field6 = `${data.fushe}`;
-  ctx.fillText(field6, currentX, currentY);
+  drawImageInRect(ctx, bottomRect, data.fusheImg, startXPercent+26 + 18, 7 + 10, 5, 30)
+  drawTextInRect(ctx, bottomRect,`${data.fushe}`,startXPercent+31 + 18, 28 + 10, 20)
+  //绘制位置信息
+  ctx.fillStyle = "#FFFFFF90";
+  drawTextInRect(ctx, bottomRect,`${formatDate(new Date(data.uploadDate))}_${data.treeCode}_S3_SCS3-3_D0P0G1`,startXPercent +1.7, 75, 16)
 
-  // 绘制文本信息
-  currentX =imgWidth - 40;
-  currentY =imgHeight - 40;
-  ctx.drawImage(data.feiniao, currentX, currentY, 25, 25);
-  ctx.fillText(data.shotCode, currentX - 10, currentY + 35);
+  if(data.baseMap){
+    drawBorderImageInRect(ctx, imgRect, data.baseMap, 2/3*100, 2/3*100,
+        1/3*100, 1/3*100, 5, 5)
+  }
 }
 
-//绘制文字
-const drawText = (ctx, textObject) => {
-  const { x, y, text, color } = textObject;
-  ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
-  ctx.font = 'normal 12px sans-serif';
-  const textHeight = parseInt(ctx.font, 10); // 获取字体大小计算文本的实际高度
-  ctx.fillText(text, x, y + textHeight); // 将y调整为y + textHeight  // 在指定位置(左上角)绘制文本
-};
 
 const showTagBox = ref(true); // 控制 tag-box 的显示状态
 const hideTagBox = (event) => {
@@ -205,6 +146,15 @@ const hideTagBox = (event) => {
   showTagBox.value = false; // 隐藏 tag-box
 };
 
+const formatDate = (date) => {
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
+  const day = String(date.getDate()).padStart(2, '0');
+  return `${(year+"").substring(2)}${month}${day}`;
+};
+
+
+
 
 </script>
 
@@ -304,5 +254,24 @@ canvas {
   transform: translateY(-50%);
 }
 
+.floating-img {
+  position: absolute;
+  bottom: 0;
+  right: 0;
+  width: auto !important;
+  height: 25% !important;
+}
+.floating-img-big {
+  position: fixed !important;
+  z-index: 99999 !important;
+  top: 50% !important;
+  left: 50% !important;
+  width: auto !important;
+  height: 100% !important;
+  transform: translate(-50%, -50%) !important;
+}
+
+
+
 
 </style>

+ 12 - 1
src/views/home/album_compoents/cacheImg.js

@@ -12,6 +12,7 @@ function loadImage(url, key) {
         // 如果缓存中没有,则创建一个新的图片对象
         const img = new Image();
         img.src = url;
+        img.crossOrigin = 'anonymous';
 
         img.onload = () => {
             // 图片加载完成后,将其存入缓存
@@ -59,6 +60,16 @@ loadImage(require('@/assets/watermark/temp.png'), 'temp')
         console.error('图片加载失败', error);
     });
 
-export { imageCache };
+loadImage("https://birdseye-img.sysuimars.com/birdseye-look-mini/base_map/90378.jpg", 'base_map')
+    .then((img) => {
+        // 在这里使用加载完成的图片
+        // console.log('图片加载成功', img);
+    })
+    .catch((error) => {
+        console.error('图片加载失败', error);
+    });
+
+
+export { imageCache ,loadImage};
 
 

+ 256 - 0
src/views/home/album_compoents/utils.js

@@ -0,0 +1,256 @@
+/**
+ * 在矩形内绘制矩形
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {number} startXPercent - 内部矩形起始点横坐标,范围从 0 到 100
+ * @param {number} startYPercent - 内部矩形起始点纵坐标,范围从 0 到 100
+ * @param {number} widthPercent - 内部矩形宽度,范围从 0 到 100
+ * @param {number} heightPercent - 内部矩形高度,范围从 0 到 100
+ * @param {string} color - 填充颜色,可选参数,默认为黑色
+ */
+ const drawRectInRect = (ctx, rect, startXPercent, startYPercent, widthPercent, heightPercent, color = 'black') => {
+    // 计算内部矩形的起始坐标
+    const startX = rect.x + (startXPercent / 100) * rect.width;
+    const startY = rect.y + (startYPercent / 100) * rect.height;
+    // 计算内部矩形的宽度和高度
+    const width = (widthPercent / 100) * rect.width;
+    const height = (heightPercent / 100) * rect.height;
+    ctx.fillStyle = color; // 设置填充颜色
+    ctx.fillRect(startX, startY, width, height); // 绘制矩形
+    return { x:startX, y:startY, width, height };
+};
+
+
+
+/**
+ * 在矩形内绘制文本
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {string} text - 要绘制的文本
+ * @param {number} startXPercent - 文字起始横坐标,范围从 0 到 100
+ * @param {number} startYPercent - 文字起始纵坐标,范围从 0 到 100
+ * @param {number} fontSizePercent - 文字大小,范围从 0 到 100,基于矩形高度
+ */
+const drawTextInRect = (ctx, rect, text, startXPercent, startYPercent, fontSizePercent) => {
+    // 计算文字起始坐标
+    const startX = rect.x + (startXPercent / 100) * rect.width;
+    const startY = rect.y + (startYPercent / 100) * rect.height;
+
+    // 计算文字大小
+    const fontSize = (fontSizePercent / 100) * rect.height;
+    ctx.font = `${fontSize}px sans-serif`; // 设置字体样式
+    ctx.fillText(text, startX, startY);
+};
+
+/**
+ * 在矩形内绘制线
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {number} startXPercent - 起始点横坐标,范围从 0 到 100
+ * @param {number} startYPercent - 起始点纵坐标,范围从 0 到 100
+ * @param {number} endXPercent - 结束点横坐标,范围从 0 到 100
+ * @param {number} endYPercent - 结束点纵坐标,范围从 0 到 100
+ * @param {string} color - 线条颜色,可选参数,默认为黑色
+ * @param {number} lineWidth - 线条宽度,可选参数,默认为1像素
+ */
+const drawLineInRect = (ctx, rect, startXPercent, startYPercent, endXPercent, endYPercent, color = 'black', lineWidth = 1) => {
+    // 计算起始点坐标
+    const startX = rect.x + (startXPercent / 100) * rect.width;
+    const startY = rect.y + (startYPercent / 100) * rect.height;
+
+    // 计算结束点坐标
+    const endX = rect.x + (endXPercent / 100) * rect.width;
+    const endY = rect.y + (endYPercent / 100) * rect.height;
+
+    // 设置线条样式
+    ctx.strokeStyle = color; // 设置线条颜色
+    ctx.lineWidth = lineWidth; // 设置线条宽度
+
+    // 绘制线
+    ctx.beginPath();
+    ctx.moveTo(startX, startY);
+    ctx.lineTo(endX, endY);
+    ctx.stroke();
+};
+
+/**
+ * 在矩形内绘制点
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {number} xPercent - 点的横坐标,范围从 0 到 100
+ * @param {number} yPercent - 点的纵坐标,范围从 0 到 100
+ * @param {string} color - 点的颜色,可选参数,默认为黑色
+ * @param {number} radius - 点的半径,可选参数,默认为1像素
+ */
+const drawPointInRect = (ctx, rect, xPercent, yPercent, color = 'black', radius = 1) => {
+    // 计算点的坐标
+    const x = rect.x + (xPercent / 100) * rect.width;
+    const y = rect.y + (yPercent / 100) * rect.height;
+
+    // 设置点的样式
+    ctx.fillStyle = color; // 设置点的颜色
+
+    // 开始绘制路径
+    ctx.beginPath();
+    ctx.arc(x, y, radius, 0, Math.PI * 2); // 绘制一个圆
+    ctx.fill(); // 填充圆
+};
+
+/**
+ * 在矩形内绘制图片
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {HTMLImageElement} img - 要绘制的图片对象
+ * @param {number} imgXPercent - 图片起始横坐标,范围从 0 到 100
+ * @param {number} imgYPercent - 图片起始纵坐标,范围从 0 到 100
+ * @param {number} imgWidthPercent - 图片宽度百分比,范围从 0 到 100
+ * @param {number} imgHeightPercent - 图片高度百分比,范围从 0 到 100
+ */
+const drawImageInRect = (ctx, rect, img, imgXPercent, imgYPercent, imgWidthPercent, imgHeightPercent) => {
+    // 计算图片的起始坐标
+    const imgX = rect.x + (imgXPercent / 100) * rect.width;
+    const imgY = rect.y + (imgYPercent / 100) * rect.height;
+    // 计算图片的宽度和高度
+    const imgWidth = (imgWidthPercent / 100) * rect.width;
+    const imgHeight = (imgHeightPercent / 100) * rect.height;
+    // 开始绘制图片
+    ctx.drawImage(img, imgX, imgY, imgWidth, imgHeight);
+};
+
+/**
+ * 在矩形内绘制图片,带圆角和白色边框
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {HTMLImageElement} img - 要绘制的图片对象
+ * @param {number} imgXPercent - 图片起始横坐标,范围从 0 到 100
+ * @param {number} imgYPercent - 图片起始纵坐标,范围从 0 到 100
+ * @param {number} imgWidthPercent - 图片宽度百分比,范围从 0 到 100
+ * @param {number} imgHeightPercent - 图片高度百分比,范围从 0 到 100
+ * @param {number} borderRadius - 圆角半径,以像素为单位
+ * @param {number} borderWidth - 边框宽度,以像素为单位
+ */
+const drawBorderImageInRect = (ctx, rect, img, imgXPercent, imgYPercent, imgWidthPercent, imgHeightPercent, borderRadius, borderWidth) => {
+    // 计算图片的起始坐标
+    const imgX = rect.x + (imgXPercent / 100) * rect.width + borderWidth;
+    const imgY = rect.y + (imgYPercent / 100) * rect.height + borderWidth;
+    // 计算图片的宽度和高度
+    const imgWidth = (imgWidthPercent / 100) * rect.width - 2 * borderWidth;
+    const imgHeight = (imgHeightPercent / 100) * rect.height - 2 * borderWidth;
+
+    // 保存画布状态
+    ctx.save();
+
+    // 开始绘制圆角矩形的边框
+    ctx.beginPath();
+    ctx.moveTo(imgX + borderRadius, imgY);
+    ctx.lineTo(imgX + imgWidth - borderRadius, imgY);
+    ctx.arcTo(imgX + imgWidth, imgY, imgX + imgWidth, imgY + borderRadius, borderRadius);
+    ctx.lineTo(imgX + imgWidth, imgY + imgHeight - borderRadius);
+    ctx.arcTo(imgX + imgWidth, imgY + imgHeight, imgX + imgWidth - borderRadius, imgY + imgHeight, borderRadius);
+    ctx.lineTo(imgX + borderRadius, imgY + imgHeight);
+    ctx.arcTo(imgX, imgY + imgHeight, imgX, imgY + imgHeight - borderRadius, borderRadius);
+    ctx.lineTo(imgX, imgY + borderRadius);
+    ctx.arcTo(imgX, imgY, imgX + borderRadius, imgY, borderRadius);
+    ctx.closePath();
+
+    // 设置边框颜色和宽度
+    ctx.strokeStyle = 'white';
+    ctx.lineWidth = borderWidth;
+    ctx.stroke();
+
+    // 开始绘制圆角矩形的填充(可选,如果需要背景)
+    // ctx.fillStyle = 'transparent'; // 或者设置其他背景颜色
+    // ctx.fill();
+
+    // 开始裁剪
+    ctx.clip();
+
+    // 绘制图片
+    ctx.drawImage(img, imgX, imgY, imgWidth, imgHeight);
+
+    // 恢复画布状态
+    ctx.restore();
+};
+
+/**
+ * 在矩形内绘制横向文本列表
+ * @param {CanvasRenderingContext2D} ctx - 画布的上下文
+ * @param {Object} rect - 矩形范围,包含 { x, y, width, height },以像素为单位
+ * @param {Array<string>} texts - 要绘制的文本数组
+ * @param {number} fontSizePercent - 文字大小,范围从 0 到 100,基于矩形高度
+ * @param {number} startXPercent - 文字起始横坐标,范围从 0 到 100,基于矩形宽度
+ * @param {number} startYPercent - 文字起始纵坐标,范围从 0 到 100,基于矩形高度
+ * @param {number} columnSpacingPercent - 列间距,范围从 0 到 100,基于矩形宽度
+ * @param {string} columnSeparator - 列间隔字符,默认为空字符串
+ * @param {number} columnSeparatorWidthPercent - 列间隔字符的宽度百分比,范围从 0 到 100,基于矩形宽度
+ * @param {number} separatorFontSizePercent - 分隔符字号大小,范围从 0 到 100,基于矩形高度
+ * @param {string} separatorColor - 分隔符颜色,默认为黑色
+ * @param {number} separatorMarginLeftPercent - 分隔符左边的边距,范围从 0 到 100,基于矩形宽度
+ * @param {number} separatorMarginRightPercent - 分隔符右边的边距,范围从 0 到 100,基于矩形宽度
+ */
+const drawHorizontalTextList = (ctx, rect,color='white', texts, fontSizePercent, startXPercent = 0, startYPercent = 50, columnSpacingPercent = 0, columnSeparator = '', columnSeparatorWidthPercent = 0, separatorFontSizePercent = fontSizePercent, separatorColor = 'black', separatorMarginLeftPercent = 0, separatorMarginRightPercent = 0) => {
+    // 计算文字大小
+    const fontSize = (fontSizePercent / 100) * rect.height;
+    ctx.font = `${fontSize}px sans-serif`; // 设置字体样式
+
+    // 计算文字的总宽度
+    let totalTextWidth = 0;
+    texts.forEach(text => {
+        totalTextWidth += ctx.measureText(text).width;
+    });
+
+    // 计算分隔符的字体大小
+    const separatorFontSize = (separatorFontSizePercent / 100) * rect.height;
+
+    // 计算分隔符的总宽度
+    ctx.font = `${separatorFontSize}px sans-serif`; // 设置分隔符字体样式
+    const separatorWidth = (columnSeparatorWidthPercent / 100) * rect.width;
+    const totalSeparatorWidth = (texts.length - 1) * separatorWidth;
+
+    // 计算分隔符的左右边距
+    const separatorMarginLeft = (separatorMarginLeftPercent / 100) * rect.width;
+    const separatorMarginRight = (separatorMarginRightPercent / 100) * rect.width;
+
+    // 计算可用的总间距
+    const totalSpacing = (columnSpacingPercent / 100) * rect.width;
+
+    // 计算所有文字和间隔字符以及间距的总宽度
+    const totalWidth = totalTextWidth + totalSeparatorWidth + totalSpacing + (texts.length - 1) * (separatorMarginLeft + separatorMarginRight);
+
+    // 计算实际的起始坐标,使得文本和间隔字符居中于矩形
+    const startX = rect.x + (startXPercent / 100) * rect.width + (rect.width - totalWidth) / 2;
+    const startY = rect.y + (startYPercent / 100) * rect.height - (fontSize / 2); // 绘制文字,纵坐标居中
+
+    // 绘制文本和间隔字符
+    let currentX = startX;
+    texts.forEach((text, index) => {
+        ctx.font = `${fontSize}px sans-serif`; // 设置文字字体样式
+        ctx.fillStyle = color; // 设置文字颜色
+        ctx.fillText(text, currentX, startY + (fontSize / 2)); // 绘制文字,纵坐标居中
+        currentX += ctx.measureText(text).width;
+
+        // 如果不是最后一列,则绘制间隔字符
+        if (index < texts.length - 1) {
+            // 添加左边距
+            currentX += separatorMarginLeft;
+
+            // 设置分隔符颜色和字体样式
+            ctx.fillStyle = separatorColor;
+            ctx.font = `${separatorFontSize}px sans-serif`;
+
+            // 绘制分隔符
+            ctx.fillText(columnSeparator, currentX, startY + (separatorFontSize / 2));
+
+            // 添加右边距
+            currentX += separatorWidth + separatorMarginRight + columnSpacingPercent / 100 * rect.width;
+        }
+    });
+};
+
+
+
+
+
+
+export { drawTextInRect, drawLineInRect, drawPointInRect ,drawImageInRect,drawBorderImageInRect, drawRectInRect, drawHorizontalTextList};
+

+ 22 - 21
src/views/home/index.vue

@@ -47,7 +47,7 @@
         <div class="list">
           <chart-box name="农事列表" arrow="arrow-left" :class="{'list-wrap': rightIndex===0}">
             <template v-if="rightIndex===0">
-              <album></album>
+              <!-- <album></album> -->
               <!-- <img class="tabs" src="@/assets/images/home/ns-tabs.png" alt="">
               <div class="img-box">
                 <img @click="handleAct(item)" v-for="item in 2" :key="item" :src="require(`@/assets/images/home/0${act<=2&&act==item?item+'-act':item}.png`)" alt="">
@@ -143,7 +143,7 @@ onMounted(() => {
   homeMap.initMap("POINT(113.61448114737868 23.585550924763083)", mapRef.value);
   // homeMap.initMap(store.getters.userinfo.location, mapRef.value);
   samplePointLayer = new SamplePointLayer(homeMap.kmap.map, currentFarm, currentRegion)
-  regionLayer = new RegionLayer(homeMap.kmap.map, currentFarm, currentRegion)
+  // regionLayer = new RegionLayer(homeMap.kmap.map, currentFarm, currentRegion)
 
   getYellow()
   getFarmLog()
@@ -294,29 +294,30 @@ const showType = ref("point")
 //地图点击事件
 eventBus.on('click:point',(e)=>{
   showType.value = "point"
-  if(btnIndex.value===1){
-    btnName.value=e.name
-    if(e.value===1){
-      showPoint.value = true
-      samplePointLayer.updatePointStatus(true)
-    }else{
-      showPoint.value = false
-    }
-  }
+  console.log('e',e);
+  // if(btnIndex.value===1){
+  //   btnName.value=e.name
+  //   if(e.value===1){
+  //     showPoint.value = true
+  //     samplePointLayer.updatePointStatus(true)
+  //   }else{
+  //     showPoint.value = false
+  //   }
+  // }
 })
 
 //地图点击区域事件
 eventBus.on('click:area',(e)=>{
-  showType.value = "area"
-  if(btnIndex.value===0){
-    btnName.value=e.name
-    if(e.value===1){
-      showPoint.value = true
-    }else{
-      showPoint.value = false
-    }
-    regionLayer.selectArea((e.name*1)===0?0: (e.name*1) - 1,"blue")
-  }
+  // showType.value = "area"
+  // if(btnIndex.value===0){
+  //   btnName.value=e.name
+  //   if(e.value===1){
+  //     showPoint.value = true
+  //   }else{
+  //     showPoint.value = false
+  //   }
+  //   regionLayer.selectArea((e.name*1)===0?0: (e.name*1) - 1,"blue")
+  // }
 })
 
 eventBus.on('click:updateArea',(e)=>{

+ 12 - 14
src/views/home/map/samplePointLayer.js

@@ -52,14 +52,14 @@ class SamplePointLayer {
       },
     })
     map.addLayer(this.treeClusterLayer.layer)
-    map.addLayer(this.yellowBlockLayer.layer);
+    // map.addLayer(this.yellowBlockLayer.layer);
 
     let point = new Feature(new Point([113.61396985128522,23.5859386716038]));
     let point1 = new Feature(new Point([113.61390710255375 ,23.586379215663726]));
     let point2 = new Feature(new Point([113.61491218688275 ,23.58671519555776]));
-    this.yellowBlockLayer.addFeature(point);
-    this.yellowBlockLayer.addFeature(point1);
-    this.yellowBlockLayer.addFeature(point2);
+    // this.yellowBlockLayer.addFeature(point);
+    // this.yellowBlockLayer.addFeature(point1);
+    // this.yellowBlockLayer.addFeature(point2);
 
     this.initData(this.farmId, this.regionId)
     this.addMapSingerClick(map);
@@ -93,8 +93,7 @@ class SamplePointLayer {
 
   initData(farmId, regionId){
     let that = this
-    VE_API.sample.list({farmId:766,regionId:2}).then(({data})=>{
-      // data[0].status = 9
+    VE_API.image.pointByRegionId({farmId:766,regionId:2}).then(({data})=>{
       let features = []
       for(let item of data){
         item.iconName='defalut'
@@ -125,7 +124,7 @@ class SamplePointLayer {
           if(that.isUpdatePoint){
             fs.set("iconName", "active");
           }
-          eventBus.emit("click:point",{name:fs.get("id"),value:fs.get("highYield")})
+          eventBus.emit("click:point",{farmId:fs.get("farmId"),sampleId:fs.get("sampleId")})
         }
         if (layer instanceof VectorLayer && layer.get("name") === "yellow-block") {
           hasFeature = true
@@ -168,18 +167,17 @@ class SamplePointLayer {
   }
 
   getIcon(item){
-    let imgSrc = require(`@/assets/images/map/${item.iconName}-icon.png`)
-    let scale = 0.25
+    // let imgSrc = require(`@/assets/images/map/${item.iconName}-icon.png`)
+    let imgSrc = require('@/assets/images/map/status/status-bh.png')
+    let scale = 0.7
     if(item.status == 1){
-      imgSrc = require('@/assets/images/map/active-icon.png')
+      imgSrc = require('@/assets/images/map/status/status-szyc.png')
     }
     if(item.status == 2){
-      imgSrc = require('@/assets/status/status_szyc.png')
-      scale = 0.8
+      imgSrc = require('@/assets/images/map/status/status-bh.png')
     }
     if(item.status == 3){
-      imgSrc = require('@/assets/status/status_bcyc.png')
-      scale = 0.8
+      imgSrc = require('@/assets/images/map/status/status-ch.png')
     }
     item["icon"] = imgSrc
     item["scale"] = scale

+ 2 - 1
src/views/varietyMap/index.vue

@@ -35,7 +35,7 @@
                                 {{ city.label }}
                             </el-checkbox>
                         </el-checkbox-group>
-                        <div class="add">
+                        <div class="add" @click="handleAdd">
                             <el-icon class="icon"><Plus /></el-icon>
                             添加品种
                         </div>
@@ -272,6 +272,7 @@ const handleClose = () => {
                         display: flex;
                         align-items: center;
                         padding: 8px;
+                        cursor: pointer;
                         .icon{
                             margin-right: 4px;
                         }

+ 169 - 26
src/views/varietyMap/varietyMap.js

@@ -4,11 +4,14 @@ import * as KMap from "@/utils/ol-map/KMap";
 import Stroke from "ol/style/Stroke";
 import * as util from "@/common/ol_common.js";
 import VectorSource from 'ol/source/Vector';
-import {Point } from 'ol/geom';
+import {newPoint} from "../zhgl/map";
 import Feature from "ol/Feature";
 import Draw from 'ol/interaction/Draw';
-import VectorLayer from 'ol/layer/Vector';
-import { Circle as CircleStyle, Fill, Style } from 'ol/style';
+import { Style, Fill, Circle as CircleStyle } from 'ol/style';
+import { Polygon } from 'ol/geom';
+import Icon from "ol/style/Icon";
+import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
+import {Cluster} from "ol/source";
 
 /**
  * @description
@@ -18,9 +21,32 @@ class VarietyMap {
     let that = this;
     let vectorStyle = new KMap.VectorStyle();
     this.vectorStyle = vectorStyle;
+    this.drawInteraction = null;
+    this.vectorSource = new VectorSource();
+    this.clusterSource = new Cluster({
+      distance: 15,
+      minDistance: 60,
+    });
+
+    this.centerPointLayer = new KMap.VectorLayer("centerPointLayer", 1000, {
+      source:this.clusterSource,
+      style: (f) => {
+        return new Style({
+          image: new Icon({
+            src: require('@/assets/images/map/active-icon.png'),
+            scale: 0.4,
+          }),
+        });
+      },
+    });
+  }
 
+  initMap(location, target) {
+    let level = 16;
+    let coordinate = util.wktCastGeom(location).getFirstCoordinate();
+    this.kmap = new KMap.Map( target, level, coordinate[0], coordinate[1], null, 1, 22 ,"vec", true, true);
     // 添加矢量图层用于显示框选结果
-    this.vectorLayer = new VectorLayer({
+    const vectorLayer = new VectorLayer({
       source: this.vectorSource,
       style: new Style({
         fill: new Fill({
@@ -38,33 +64,150 @@ class VarietyMap {
         }),
       }),
     });
+    this.kmap.addLayer(this.centerPointLayer.layer);
 
+    this.kmap.addLayer(vectorLayer);
+    // this.startDrawing(this.kmap.map)
+    
+
+    // // 初始化框选交互
+    this.draw = new Draw({
+      source: this.vectorSource,
+      type: 'Circle', // 框选类型
+      // geometryFunction: (coordinates, geometry) => {
+      //   if (coordinates.length < 2) {
+      //     return null;
+      //   }
+      //   return this.drawSquare(coordinates); // 绘制矩形
+      // },
+    });
+
+    // 添加框选交互到地图
+    this.kmap.map.addInteraction(this.draw);
+
+    this.initData()
+
+    // 监听drawend事件,获取框选范围
+    let that = this
+    this.draw.on('drawend', (event) => {
+      // const feature = event.feature;
+      let pixel=that.kmap.map.getEventPixel(event.originalEvent)
+      const geometry = feature.getGeometry();
+      const extent = geometry.getExtent();
+      let feature=that.kmap.map.forEachFeatureAtPixel(pixel,function (feature) {
+        console.log('feature',feature);
+        return feature
+      })
+      // if(feature != null && feature == vm.polyline){
+      //     that.pointmoveFeature = feature
+      //     callback(vm.polyline)
+      // }
+      // if(feature==undefined){
+      //     if(that.pointmoveFeature){
+      //         that.pointmoveFeature = null
+      //         callback(null)
+      //     }
+      // }
+      console.log('Selected extent:', extent);
+      console.log('Center of selected area:', getCenter(extent));
+    });
   }
 
-  initMap(location, target) {
-    let level = 16;
-    let coordinate = util.wktCastGeom(location).getFirstCoordinate();
-    this.kmap = new KMap.Map( target, level, coordinate[0], coordinate[1], null, 1, 22 ,"vec", true, true);
-    this.kmap.addLayer(this.vectorLayer);
-    // 初始化框选交互
-    // this.draw = new Draw({
-    //   source: this.vectorSource,
-    //   type: 'box', // 框选类型
-    // });
-
-    // // 添加框选交互到地图
-    // this.kmap.addInteraction(this.draw);
-
-    // // 监听drawend事件,获取框选范围
-    // this.draw.on('drawend', (event) => {
-    //   const feature = event.feature;
-    //   const geometry = feature.getGeometry();
-    //   const extent = geometry.getExtent();
-    //   console.log('Selected extent:', extent);
-    //   console.log('Center of selected area:', getCenter(extent));
-    // });
+  initData(farmId, regionId){
+    let that = this
+    VE_API.sample.list({farmId:766,regionId:2}).then(({data})=>{
+      // data[0].status = 9
+      let features = []
+      for(let item of data){
+        let point = newPoint(item);
+        features.push(point)
+      }
+      const source = new VectorSource({
+        features: features,
+      });
+      that.clusterSource.setSource(source)
+    })
   }
 
+  // 绘制矩形
+   drawRectangle = (coordinates) => {
+    const start = coordinates[0];
+    const end = coordinates[1];
+    const minX = Math.min(start[0], end[0]);
+    const maxX = Math.max(start[0], end[0]);
+    const minY = Math.min(start[1], end[1]);
+    const maxY = Math.max(start[1], end[1]);
+
+    // 定义矩形的四个顶点
+    const rectangleCoordinates = [
+      [minX, minY],
+      [maxX, minY],
+      [maxX, maxY],
+      [minX, maxY],
+      [minX, minY], // 闭合矩形
+    ];
+
+    return new Polygon([rectangleCoordinates]);
+  };
+
+    drawSquare = (coordinates) => {
+      const start = coordinates[0];
+      const end = coordinates[1];
+      const dx = end[0] - start[0];
+      const dy = end[1] - start[1];
+      const size = Math.max(Math.abs(dx), Math.abs(dy));
+    
+      const minX = start[0];
+      const minY = start[1];
+      const maxX = minX + size * Math.sign(dx);
+      const maxY = minY + size * Math.sign(dy);
+    
+      // 定义正方形的四个顶点
+      const squareCoordinates = [
+        [minX, minY],
+        [maxX, minY],
+        [maxX, maxY],
+        [minX, maxY],
+        [minX, minY], // 闭合正方形
+      ];
+    
+      return new Polygon([squareCoordinates]);
+    };
+
+
+  startDrawing = (map) => {
+    if (this.drawInteraction) {
+      map.removeInteraction(this.drawInteraction);
+    }
+
+    // 自定义绘制逻辑
+    this.drawInteraction = new Draw({
+      source: this.vectorSource,
+      type: 'Circle', // 使用 Circle 作为基础
+      geometryFunction: (coordinates, geometry) => {
+        if (coordinates.length < 2) {
+          return null;
+        }
+        return this.drawRectangle(coordinates); // 绘制矩形
+      },
+    });
+
+    map.addInteraction(this.drawInteraction);
+
+    // 监听绘制完成事件
+    this.drawInteraction.on('drawend', (event) => {
+      const feature = event.feature;
+      const geometry = feature.getGeometry();
+
+      console.log('正多边形坐标:', geometry.getCoordinates());
+    });
+  };
+
+  // 清除绘制
+  clearDrawing = () => {
+    this.vectorSource.clear();
+  };
+
   // 重新渲染地图
   updateMap() {
     setTimeout(() => {

Datei-Diff unterdrückt, da er zu groß ist
+ 1152 - 1139
yarn.lock


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.