Browse Source

feat:添加有味指数地图

wangsisi 5 days ago
parent
commit
bc9d797a26
53 changed files with 8417 additions and 94 deletions
  1. 1 0
      .env.development
  2. 1 0
      .env.production
  3. 2082 91
      package-lock.json
  4. 17 2
      package.json
  5. 33 0
      src/api/config.js
  6. 5 0
      src/api/eventBus.js
  7. 41 0
      src/api/index.js
  8. 18 0
      src/api/modules/home.js
  9. 28 0
      src/api/modules/system.js
  10. BIN
      src/assets/images/map/point-bg-active.png
  11. BIN
      src/assets/images/map/point-bg.png
  12. 94 0
      src/common/commonFun.js
  13. 165 0
      src/common/vectorStyle.js
  14. 4 0
      src/components/layout/Header.vue
  15. 1 1
      src/main.js
  16. 117 0
      src/plugins/axios.js
  17. 50 0
      src/plugins/common.js
  18. 23 0
      src/styles/common.scss
  19. 30 0
      src/utils/index.js
  20. 204 0
      src/utils/map.js
  21. 78 0
      src/utils/ol-map/Bounds.js
  22. 63 0
      src/utils/ol-map/Check.js
  23. 143 0
      src/utils/ol-map/Circle.js
  24. 145 0
      src/utils/ol-map/Common.js
  25. 37 0
      src/utils/ol-map/CustomLayer.js
  26. 9 0
      src/utils/ol-map/Enum.js
  27. 19 0
      src/utils/ol-map/Info.js
  28. 191 0
      src/utils/ol-map/InfoWindow.js
  29. 11 0
      src/utils/ol-map/KBaseObject.js
  30. 38 0
      src/utils/ol-map/KMap.js
  31. 313 0
      src/utils/ol-map/Label.js
  32. 78 0
      src/utils/ol-map/LngLat.js
  33. 1318 0
      src/utils/ol-map/Map.js
  34. 566 0
      src/utils/ol-map/Marker.js
  35. 108 0
      src/utils/ol-map/MultiPolygon.js
  36. 55 0
      src/utils/ol-map/Pixel.js
  37. 108 0
      src/utils/ol-map/Polygon.js
  38. 297 0
      src/utils/ol-map/Polyline.js
  39. 101 0
      src/utils/ol-map/Popup.js
  40. 39 0
      src/utils/ol-map/Size.js
  41. 87 0
      src/utils/ol-map/VTLayer.js
  42. 98 0
      src/utils/ol-map/VectorLayer.js
  43. 211 0
      src/utils/ol-map/VectorStyle.js
  44. 327 0
      src/utils/ol-map/WMSLayer.js
  45. 111 0
      src/utils/ol-map/WMTSLayer.js
  46. 108 0
      src/utils/ol-map/XYZLayer.js
  47. 88 0
      src/utils/ol-map/css/KMap.css
  48. BIN
      src/utils/ol-map/css/rotate.png
  49. 123 0
      src/utils/ol_common.js
  50. 89 0
      src/utils/turf_util.js
  51. 2 0
      src/views/Index.vue
  52. 139 0
      src/views/pages/ExponentialMap.vue
  53. 403 0
      src/views/pages/map/mockFarmLayer.js

+ 1 - 0
.env.development

@@ -0,0 +1 @@
+VUE_APP_API_URL = "https://feiniaotech-dev.sysuimars.cn/"

+ 1 - 0
.env.production

@@ -0,0 +1 @@
+VITE_API_BASE_URL = "https://birdseye-api.feiniaotech.sysuimars.cn/"

File diff suppressed because it is too large
+ 2082 - 91
package-lock.json


+ 17 - 2
package.json

@@ -3,15 +3,25 @@
   "version": "0.1.0",
   "private": true,
   "scripts": {
-    "serve": "vue-cli-service serve",
+    "servedev": "vue-cli-service serve --mode development",
+    "servepro": "vue-cli-service serve --mode production",
     "build": "vue-cli-service build",
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.3.1",
+    "@turf/turf": "^7.1.0",
+    "axios": "^1.9.0",
     "core-js": "^3.8.3",
     "element-plus": "^2.8.4",
+    "html2canvas": "^1.4.1",
+    "jsts": "^2.12.1",
+    "mitt": "^3.0.1",
+    "ol": "^10.5.0",
+    "ol-ext": "^4.0.32",
+    "proj4": "^2.17.0",
     "vue": "^3.2.13",
+    "vue-cli-plugin-axios": "^0.0.4",
     "vue-router": "^4.4.5"
   },
   "devDependencies": {
@@ -44,5 +54,10 @@
     "last 2 versions",
     "not dead",
     "not ie 11"
-  ]
+  ],
+  "lint-staged": {
+    "*.{js,jsx,vue}": [
+      "vue-cli-service lint --fix"
+    ]
+  }
 }

+ 33 - 0
src/api/config.js

@@ -0,0 +1,33 @@
+let serverMock = "https://mock.apipark.cn/m1/4662471-4313509-default/"
+let djiCloudBase = "https://djiapi.sysuimars.com/";
+
+const config =  {
+    server_mock :serverMock + "site/",
+    dji_cloud_base :djiCloudBase,
+    mini_key:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9",
+    base_img_url : "https://img.sysuimars.com/",
+    base_img_url2 : "https://birdseye-img-ali-cdn.sysuimars.com/",
+    base_img_url3 : "https://birdseye-img.sysuimars.com/",
+    base_video_url : "https://minio.sysuimars.com/sysuimars/",
+    //获取请求头中的参数体
+    getOptBody : (opt)=>{
+        return JSON.parse(opt.body);
+    },
+    //需要忽略成功提示的请求
+    igSuccessUrl:[
+        "update",
+        "home",
+        "get",
+        "page",
+        "sign",
+        "List",
+        "info",
+        "list",
+        "air_route",
+        "find",
+        "poi",
+        "land_check",
+    ]
+}
+
+export default config

+ 5 - 0
src/api/eventBus.js

@@ -0,0 +1,5 @@
+import mitt from 'mitt';
+
+const eventBus = mitt();
+
+export default eventBus;

+ 41 - 0
src/api/index.js

@@ -0,0 +1,41 @@
+const path = require("path");
+const fs = require("fs");
+const getPathInfo = (p) => path.parse(p);
+
+/**
+ * @description // 递归读取文件,类似于webpack的require.context()
+ *
+ * @param {String} directory 文件目录
+ * @param {Boolean} useSubdirectories 是否查询子目录,默认false
+ * @param {array} extList 查询文件后缀,默认 ['.js']
+ *
+ */
+function autoLoadFile(directory, useSubdirectories = false, extList = [".js"]) {
+    const filesList = {};
+    // 递归读取文件
+    function readFileList(directory, useSubdirectories, extList) {
+        const files = fs.readdirSync(directory);
+        files.forEach((item) => {
+            const fullPath = path.join(directory, item);
+            const stat = fs.statSync(fullPath);
+            if (stat.isDirectory() && useSubdirectories) {
+                readFileList(
+                    path.join(directory, item),
+                    useSubdirectories,
+                    extList
+                );
+            } else {
+                const info = getPathInfo(fullPath);
+
+                if (extList.includes(info.ext)) {
+                    filesList[info.name] = require(fullPath);
+                }
+            }
+        });
+    }
+    readFileList(directory, useSubdirectories, extList);
+
+    return filesList;
+}
+
+module.exports = autoLoadFile(path.join(__dirname, "./modules"));

+ 18 - 0
src/api/modules/home.js

@@ -0,0 +1,18 @@
+import axios from "@/plugins/axios";
+
+// 点亮地图
+export function map() {
+    return axios({
+        url: `/z_farm_buy_shop_noauth/map`,
+        method: 'get',
+        prefixUrl:'/site',
+    })
+}
+
+export function featchShopList() {
+    return axios({
+        url: `/z_farm_buy_shop_noauth/list`,
+        method: 'get',
+        prefixUrl:'/site',
+    })
+}

+ 28 - 0
src/api/modules/system.js

@@ -0,0 +1,28 @@
+import axios from "@/plugins/axios";
+
+// 地图ol-map
+export function getCfg(data) {
+    return axios({
+        url: `/cfg/get?key=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9`,
+        method: 'post',
+        data
+    })
+}
+
+//搜索
+export function getCtiyList(params) {
+    return axios({
+        url: `/poi/city`,
+        method: 'get',
+        params
+    })
+}
+
+export function search(params) {
+    return axios({
+        url: `/ws/place/v1/suggestion`,
+        method: 'get',
+        prefixUrl:'/ws',
+        params
+    })
+}

BIN
src/assets/images/map/point-bg-active.png


BIN
src/assets/images/map/point-bg.png


+ 94 - 0
src/common/commonFun.js

@@ -0,0 +1,94 @@
+
+import html2canvas from "html2canvas";
+
+// 深拷贝
+function deepClone(obj) {
+  if (typeof obj !== "object" || obj === null) {
+    return obj;
+  }
+  const target = obj.constructor === Array ? [] : {};
+  for (var key in obj) {
+    if (Object.prototype.hasOwnProperty.call(obj, key)) {
+      if (typeof obj[key] === "object") {
+        target[key] = deepClone(obj[key]);
+      } else {
+        target[key] = obj[key];
+      }
+    }
+  }
+  return target;
+}
+
+function extractCoordinates(input) {
+  // 使用正则表达式匹配括号内的内容
+  const match = input.match(/\(([^)]+)\)/);
+
+  if (match) {
+    // 如果找到了匹配项,match[1] 将包含括号内的内容
+    // 然后我们按照空格分割这个字符串来获取坐标
+    const coordinates = match[1].split(" ").map(Number); // 将每个坐标转换为数字
+    return coordinates;
+  }
+
+  // 如果没有找到匹配项,返回null或其他合适的值
+  return null;
+}
+
+// 节流 => 一定时间内只调用一次函数
+function throttle(func, wait) {
+  let timeout = null;
+  let lastRun = 0;
+
+  return function (...args) {
+    const now = new Date().getTime();
+
+    if (!lastRun) {
+      // 如果 lastRun 没有被设置,表示这是第一次调用
+      func.apply(this, args);
+      lastRun = now;
+    } else {
+      clearTimeout(timeout);
+      // 设置一个新的超时,在 wait 时间后再次运行函数
+      timeout = setTimeout(() => {
+        if (now - lastRun >= wait) {
+          func.apply(this, args);
+          lastRun = now;
+        }
+      }, wait - (now - lastRun));
+    }
+  };
+}
+
+
+function convertImage(imgUrl) {
+  return new Promise((resolve, reject) => {
+    fetch(imgUrl)
+      .then(response => {
+        if (!response.ok) {
+          throw new Error(`HTTP 错误:${response.status}`);
+        }
+        return response.blob(); // 获取图片的二进制数据
+      })
+      .then(blob => {
+        const reader = new FileReader();
+
+        reader.onload = () => {
+          // 转换成功后返回 Base64 数据
+          resolve(reader.result); // 通过 resolve 返回 Base64 数据
+        };
+
+        reader.onerror = (error) => {
+          console.error("文件读取失败:", error);
+          reject(error); // 出现错误时通过 reject 抛出错误
+        };
+
+        reader.readAsDataURL(blob); // 将 Blob 数据转换为 Base64
+      })
+      .catch(error => {
+        console.error("图片转换失败:", error);
+        reject(error); // 捕获错误并通过 reject 抛出
+      });
+  });
+}
+
+export { deepClone, extractCoordinates, throttle, convertImage };

+ 165 - 0
src/common/vectorStyle.js

@@ -0,0 +1,165 @@
+import Style from 'ol/style/Style.js';
+import Fill from 'ol/style/Fill.js';
+import Text from 'ol/style/Text.js';
+import Stroke from 'ol/style/Stroke.js';
+import Icon from 'ol/style/Icon.js';
+import Circle from "ol/style/Circle";
+
+function cityLand(gridcode) {
+    let fillColor = ["#6E7F8580","#7B9CFB80","#979B4980","#E9920980","#182F1380","#30791380","#0D53AF80"];
+    return new Style({
+        fill: new Fill({
+            color: fillColor[gridcode],
+        })
+    });
+}
+function pointStyle(f){
+    let style = new Style({
+        text: new Text({
+            text:f.getId() ? f.getId() : f.get("id"),
+            stroke: new Stroke({
+                color: 'rgba(239,236,236)',
+                width: 1,
+            }),
+            fill: new Fill({
+                color: 'rgba(239,236,236)',
+            }),
+            font:"14px sans-serif"
+        }),
+        image: new Icon({
+            src:"../status_"+f.get("status")+".png",
+            scale:0.3,
+            anchor:[0.5,1],
+        })
+    });
+    return style
+}
+
+function areaStyle(f){
+    let style = new Style({
+        fill: new Fill({
+            color: f.get("color")+"10"
+        }),
+        stroke: new Stroke({
+            color: f.get("color"),
+            width: 1,
+        }),
+    });
+    return style;
+}
+
+
+let selectStyle = (f)=>{
+    if(f.get("nodeType") === "area"){
+        return areaStyle(f)
+    }
+
+    if(f.get("nodeType") === "town"){
+        return cropStyle(f)
+    }
+
+    let color = f.get("color");
+    let radius = 10;
+    if(!color || color == ""){
+        color = "green"
+        radius = 5;
+    }
+    let style = new Style({
+        image: new Circle({
+            radius: radius,                             // 半径
+            stroke: new Stroke({           // 边界样式
+                color: 'red',                    // 边界颜色
+                width: 3                            // 边界宽度
+            }),
+            fill: new Fill({               // 填充样式
+                color: color                       // 填充颜色
+            })
+        })
+    });
+    return style
+}
+
+
+function cropStyle(f){
+    const fill = new Fill({
+        color: 'rgba(255,255,255,0.1)',
+    });
+    return new Style({
+        fill:fill
+    });
+}
+function selectedCropStyle(f){
+    if(f.get("isPoint")){
+        return pointStyle(f)
+    }
+    const stroke = new Stroke({
+        color: 'rgb(10,241,23)',
+        width: 2
+    });
+    return new Style({
+        stroke:stroke
+    });
+};
+
+function fitPointStyle(f){
+    if(f.get("nodeType") == "fitPoint"){
+        let color = "#ffffff"
+        let radius = 2;
+        if(f.get("sort") == 0){
+            color = "yellow";
+            radius = "6"
+        }
+        let style = new Style({
+            image: new Circle({
+                radius: radius,                           // 半径
+                fill: new Fill({               // 填充样式
+                    color:  color// 填充颜色
+                })
+            })
+        });
+        return style
+    }else{
+        let style = new Style({
+            fill: new Fill({               // 填充样式
+                color:  '#23DB9E'// 填充颜色
+            }),
+            stroke: new Stroke({
+                color: "#23DB9E",
+                width: 2,
+            }),
+        });
+        return style
+    }
+}
+
+function numberPointStyle(f){
+    if(f.get("nodeType") == "fitPoint") {
+        let style = new Style({
+            text: new Text({
+                offsetY:-10,
+                offsetX:-10,
+                text: f.get("sort")+"",
+                stroke: new Stroke({
+                    color: 'red',
+                    width: 1,
+                }),
+                fill: new Fill({
+                    color: 'red',
+                }),
+                font: "12px sans-serif"
+            })
+        });
+        return style
+    }
+}
+
+
+export {
+    cityLand,
+    selectedCropStyle,
+    cropStyle,
+    pointStyle,
+    selectStyle,
+    fitPointStyle,
+    numberPointStyle,
+}

+ 4 - 0
src/components/layout/Header.vue

@@ -45,6 +45,10 @@ const tabs = [
         name: "生态守护者联盟",
         component: "DefendLeague",
     },
+    {
+        name: "有味指数地图",
+        component: "ExponentialMap",
+    },
 ];
 
 const emit = defineEmits(["handleActive"]);

+ 1 - 1
src/main.js

@@ -12,5 +12,5 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component);
 }
 
-app.use(ElementPlus).use(router);
+app.use(ElementPlus).use(router)
 app.mount("#app");

+ 117 - 0
src/plugins/axios.js

@@ -0,0 +1,117 @@
+import axios from 'axios';
+import { tansParams } from "./common";
+import { ElMessage } from 'element-plus'
+import { useRouter } from "vue-router";
+const router = useRouter();
+
+const API_BASE_URL = process.env.VUE_APP_API_URL
+const instance = axios.create({
+  baseURL: API_BASE_URL, // 替换为你的 API 基础 URL
+  timeout: 10000, // 请求超时时间
+  headers: { 'Content-Type': 'application/json' }, // 默认请求头
+});
+
+// 添加请求拦截器
+instance.interceptors.request.use(
+  config => {
+    // 在这里可以添加一些全局的请求头,比如认证 token
+    if (localStorage.getItem('token')) {
+      config.headers.token = localStorage.getItem('token');
+    }
+    // if (token && !isToken) {
+    //   config.headers['Authorization'] = token // 让每个请求携带自定义token 请根据实际情况自行修改
+    // }
+    if(config.prefixUrl){
+      if(config.prefixUrl=='/ws'){
+        config.baseURL = ''
+      }else{
+        config.url = config.prefixUrl + config.url
+      }
+    }else{
+      config.url = '/mini' + config.url
+    }
+    // get请求映射params参数
+    // if (config.method === 'get' && config.params) {
+    //   let url = config.url + '?' + tansParams(config.params);
+    //   url = url.slice(0, -1);
+    //   config.params = {};
+    //   config.url = url;
+    // }
+    return config;
+  },
+  error => {
+    // 处理请求错误
+    return Promise.reject(error);
+  }
+);
+
+// 添加响应拦截器(可选)
+instance.interceptors.response.use(
+  response => {
+    // 对响应数据做些什么
+    let type = "";
+    if (response.data.code == 0 || response.data.code === 1) {
+        type = "success";
+    } else {
+        type = "error";
+    }
+    //igSuccess 状态为false 才需要成功提示
+    // if(type == "error" || getIsShow(response.request.responseURL) == false){
+        
+    // }
+    return response.data;
+  },
+  error => {
+    // 对响应错误做些什么,比如统一处理错误提示
+    // 可以根据http状态码进行不同的处理
+    if (error.response) {
+      let message = "";
+      switch (error.response.status) {
+        case 401:
+          message = "未授权,请登录";
+          // 未授权,跳转到登录页面
+          // router.replace({
+          //     name: "Login",
+          // });
+          break;
+        case 400:
+            message = "请求错误";
+            break;
+        case 403:
+            message = "没有权限,拒绝访问";
+            break;
+        case 404:
+            message = `请求地址出错`;
+            break;
+        case 408:
+            message = "请求超时";
+            break;
+        case 500:
+            message = "服务器内部错误";
+            break;
+        case 501:
+            message = "服务未实现";
+            break;
+        case 502:
+            message = "网关错误";
+            break;
+        case 503:
+            message = "服务不可用";
+            break;
+        case 504:
+            message = "网关超时";
+            break;
+        case 505:
+            message = "HTTP版本不受支持";
+            break;
+        default:
+            break;
+      }
+      ElMessage.error(message)
+    }
+    return Promise.reject(error);
+  }
+);
+ 
+// 导出axios实例
+export default instance;

+ 50 - 0
src/plugins/common.js

@@ -0,0 +1,50 @@
+/**
+* 参数处理
+* @param {*} params  参数
+*/
+export function tansParams(params) {
+	let result = ''
+	for (const propName of Object.keys(params)) {
+		const value = params[propName];
+		var part = encodeURIComponent(propName) + "=";
+		if (value !== null && typeof (value) !== "undefined") {
+			if (typeof value === 'object') {
+				for (const key of Object.keys(value)) {
+					if (value[key] !== null && typeof (value[key]) !== 'undefined') {
+						let params = propName + '[' + key + ']';
+						var subPart = encodeURIComponent(params) + "=";
+						result += subPart + encodeURIComponent(value[key]) + "&";
+					}
+				}
+			} else {
+				result += part + encodeURIComponent(value) + "&";
+			}
+		}
+	}
+	return result
+}
+
+export function formDate(date) {
+	let myYear = date.getFullYear();
+	let myMonth = date.getMonth() + 1;
+	let myWeekday = date.getDate();
+	let myHours = date.getHours()
+	let myMinutes = date.getMinutes()
+	let mySeconds = date.getSeconds()
+	if (myMonth < 10) {
+		myMonth = "0" + myMonth;
+	}
+	if (myWeekday < 10) {
+		myWeekday = "0" + myWeekday;
+	}
+	if (myHours < 10) {
+		myHours = "0" + myHours;
+	}
+	if (myMinutes < 10) {
+		myMinutes = "0" + myMinutes;
+	}
+	if (mySeconds < 10) {
+		mySeconds = "0" + mySeconds;
+	}
+	return myYear + "-" + myMonth + "-" + myWeekday + " " + myHours + ":" + myMinutes + ":" + mySeconds;
+}

+ 23 - 0
src/styles/common.scss

@@ -19,6 +19,29 @@
   font-style: normal; /* 字体风格 */
 }
 
+.common-dark-select-popper {
+  &.el-popper {
+      background: #232323;
+      border-color: rgba(255, 212, 137, 0.42);
+  }
+  &.el-popper.is-light .el-popper__arrow::before {
+      background: #232323;
+      border-color: rgba(255, 212, 137, 0.42);
+  }
+  .el-select-dropdown__item.is-selected {
+      color: #FFD489;
+  }
+  .el-select-dropdown__item.is-hovering {
+      background-color: rgba(255, 212, 137, 0.05);
+  }
+  .el-select-dropdown__item {
+      color: #FFFFFF;
+  }
+  .el-select__caret {
+      color: #FFD489;
+  }
+}
+
 
 //滚动条的宽度
 ::-webkit-scrollbar {

+ 30 - 0
src/utils/index.js

@@ -0,0 +1,30 @@
+export function convertToMultiPolygonWKT(coordinates) {
+    // 确保多边形闭合(首尾点相同)
+    const closedCoords = [...coordinates];
+    if (closedCoords.length > 0) {
+      const firstPoint = closedCoords[0];
+      const lastPoint = closedCoords[closedCoords.length - 1];
+      if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) {
+        closedCoords.push(firstPoint); // 闭合多边形
+      }
+    }
+  
+    // 构建 WKT MULTIPOLYGON 字符串
+    const pointsStr = closedCoords.map(coord => `${coord[0]} ${coord[1]}`).join(", ");
+    return `MULTIPOLYGON (((${pointsStr})))`;
+}
+
+export function extractCoordinates(input) {
+  // 使用正则表达式匹配括号内的内容
+  const match = input.match(/\(([^)]+)\)/);
+
+  if (match) {
+    // 如果找到了匹配项,match[1] 将包含括号内的内容
+    // 然后我们按照空格分割这个字符串来获取坐标
+    const coordinates = match[1].split(" ").map(Number); // 将每个坐标转换为数字
+    return coordinates;
+  }
+
+  // 如果没有找到匹配项,返回null或其他合适的值
+  return null;
+}

+ 204 - 0
src/utils/map.js

@@ -0,0 +1,204 @@
+import VectorSource from 'ol/source/Vector.js';
+import WKT from 'ol/format/WKT.js';
+import Feature from 'ol/Feature.js';
+import VectorLayer from 'ol/layer/Vector.js';
+import Draw from "ol/interaction/Draw";
+import Text from "ol/style/Text";
+import Icon from "ol/style/Icon";
+import {Circle, Fill, Stroke, Style} from 'ol/style.js';
+import * as proj from "ol/proj";
+import {getArea} from "ol/sphere.js";
+
+/*
+ * @Author: your name
+ * @Date: 2021-01-12 09:38:09
+ * @LastEditTime: 2022-01-20 10:37:39
+ * @LastEditors: Please set LastEditors
+ * @Description: In User Settings Edit
+ * @FilePath: \vue3-element-admin\src\utils\map.js
+ */
+/**
+ * 过滤不应该出现在属性列表的字段
+ * @param data
+ * @param key
+ * @returns {boolean}
+ */
+function filterWktProp(data,key){
+    if(key == "regionWkt" || key == "wkt" || key == "pointWkt" || key == "geom" || !data[key]){
+        return false
+    }
+    return true
+}
+
+export const newAreaFeature = (data)=>{
+    let geom = new WKT().readGeometry(data["wkt"])
+    let feature = new Feature({
+        geometry: geom
+    });
+    feature.set("nodeType","area");
+    feature.setId(data.id)
+    for(let key in data){
+        if(filterWktProp(data,key)){
+            feature.set(key,data[key])
+        }
+    }
+    return feature;
+}
+
+export const newAreaPoint = (data)=>{
+    let point = new WKT().readGeometry(data["pointWkt"])
+    let feature = new Feature({
+        geometry: point
+    });
+    feature.set("nodeType","area");
+    feature.set("isPoint",1)
+    for(let key in data){
+        if(filterWktProp(data,key)){
+            feature.set(key,data[key])
+        }
+    }
+    return feature;
+}
+
+export const newPolymerFeature = (data)=>{
+    let geom = new WKT().readGeometry(data["geom"])
+    let feature = new Feature({
+        geometry: geom
+    });
+    feature.set("nodeType","polymer");
+    feature.setId(data.id)
+    for(let key in data){
+        if(filterWktProp(data,key)){
+            feature.set(key,data[key])
+        }
+    }
+    return feature;
+}
+
+export const newRegionFeature = (data)=>{
+    let geom = new WKT().readGeometry(data["regionWkt"])
+    let feature = new Feature({
+        geometry: geom
+    });
+    feature.set("nodeType","region");
+    feature.setId(data.id)
+    for(let key in data){
+        if(filterWktProp(data,key)){
+            feature.set(key,data[key])
+        }
+    }
+    return feature;
+}
+
+
+export const newPoint = (data,dataName)=>{
+    let point = new WKT().readGeometry(data[dataName])
+    let feature = new Feature({
+        geometry: point
+    });
+    feature.setId(data.id)
+    feature.set("nodeType","tree")
+    for(let key in data){
+        if(filterWktProp(data,key)){
+            feature.set(key,data[key])
+        }
+    }
+    return feature;
+}
+
+/**
+ * 按treeId分组
+ * @param data
+ * @returns {[]}
+ */
+export const groupByTreeId = (data) => {
+    let res = []
+    let cur = {treeId:-1,data:[]}
+    for(let item of data){
+        if(cur.treeId != item.treeId){
+            cur = {treeId: item.treeId, data:[]}
+            res.push(cur)
+        }
+        cur.data.push(item)
+    }
+    for(let item of res){
+        item.data.sort((a,b) => {
+            return Date.parse(b.uploadDate) - Date.parse(a.uploadDate)
+        })
+    }
+    return res
+}
+
+/**
+ * 按createDate分组
+ * @param data
+ * @returns {[]}
+ */
+export const groupByCreateDate = (data) => {
+    let res = {}
+    let arr = []
+    for(let item of data){
+        if(res[item.uploadDate]){
+            res[item.uploadDate].push(item)
+        }else{
+            res[item.uploadDate] = [item]
+            arr.push({uploadDate:item.uploadDate, data: res[item.uploadDate]})
+        }
+    }
+    arr.sort((a,b) => {
+        return Date.parse(b.uploadDate) - Date.parse(a.uploadDate)
+    })
+    return arr
+}
+
+
+export const setPeriodAttr = (periodMap, data) => {
+    for(let item of data){
+        item["attrs"] = []
+        let periodObj = periodMap[item.periodId]
+        item["periodName"] = periodObj.name
+        for(let i=0;i<periodObj.attrFields.length;i++){
+            item["attrs"].push({name:periodObj.attrNames[i],field:periodObj.attrFields[i]})
+        }
+    }
+}
+
+
+export const bboxToFeature = ( x1,  y1,  x2,  y2) => {
+    let wkt = "POLYGON (("+x1+" "+y1+", "+x1+" "+y2+", "+x2+" "+y2+", "+x2+" "+y1+", "+x1+" "+y1+"))";
+    let feature = new Feature({
+        geometry: new WKT().readGeometry(wkt)
+    });
+    return feature
+}
+/**
+ * 红框样式
+ * @returns {Style}
+ */
+export const redBoxStyle = ()=>{
+    const fill = new Fill({
+        color: 'rgba(255,255,255,0.1)',
+    });
+    const stroke = new Stroke({
+        color: '#f5024f',
+        width: 0.5,
+    });
+    let  style = new Style({
+        fill: fill,
+        stroke: stroke,
+    })
+    return style
+}
+
+export const getAreaByWkt = (wkt)=>{
+    let area = 0;
+    let geom = new WKT().readGeometry(wkt)
+    // 获取图层上的Polygon,转成geoJson用于回显
+    geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:3857"));
+    let areaItem = getArea(geom);
+    areaItem = (areaItem + areaItem / 2) / 1000;
+    area = areaItem;
+    return area.toFixed(2)
+}
+
+

+ 78 - 0
src/utils/ol-map/Bounds.js

@@ -0,0 +1,78 @@
+import * as extent  from "ol/extent"
+import LngLat from "./LngLat"
+/**
+ * @description KMap.Bounds 经纬度矩形范围类
+ */
+class Bounds{
+  /**
+   * @param {KMap.LngLat} southWest 矩形范围西南角坐标,必填,格式new KMap.LngLat()
+   * @param {KMap.LngLat} northEast 矩形范围东北角坐标,必填,格式new KMap.LngLat()
+   */
+  constructor(southWest,northEast){
+		const vm = this
+		vm.southWest = southWest
+		vm.northEast = northEast
+    let mapSouthWest = [southWest.getLng(),southWest.getLat()]
+    let mapNorthEast = [northEast.getLng(),northEast.getLat()]
+    let bounds = new extent.boundingExtent([mapSouthWest,mapNorthEast])
+    this.bounds = bounds
+  }
+
+	/**
+	 * @description 判断指定点坐标是否在矩形范围内
+	 * @param {KMap.LngLat} point 经纬度点,KMap.LngLat格式,必填
+	 * @returns {boolean}在矩形范围内返回true,否则返回false
+	 */
+  contains(point) {
+		const vm = this
+		if( (point.getLng() >= vm.southWest.getLng() && point.getLng() <= vm.northEast.getLng())
+			&& (point.getLat() >= vm.southWest.getLat() && point.getLat() <= vm.northEast.getLat()) )
+		{
+			return true
+		}else{
+			return false
+		}
+  }
+
+  /**
+   * @description 获取中心点坐标
+   * @returns {KMap.LngLat}中心点坐标,KMap.LngLat格式
+   */
+	getCenter() {
+		const vm = this
+		let lng = (vm.southWest.getLng() + vm.northEast.getLng()) / 2
+		let lat = (vm.southWest.getLat() + vm.northEast.getLat()) / 2
+		var center = new LngLat(lng,lat)
+		return center
+	}
+
+	/**
+	 * @description 获取西南角坐标
+	 * @returns {KMap.LngLat}西南角坐标,KMap.LngLat格式
+	 */
+	getSouthWest() {
+		const vm = this
+		return vm.southWest
+	}
+
+	/**
+	 * @description 获取东北角坐标
+	 * @returns {KMap.LngLat}东北角坐标,KMap.LngLat格式
+	 */
+	getNorthEast() {
+		const vm = this
+		return vm.northEast
+	}
+
+	/**
+	 * @description 以字符串形式返回地物对象的矩形范围
+	 * @returns {String}西南角经度、西南角纬度:东北角经度、东北角纬度
+	 */
+  toString() {
+		const vm = this
+        return vm.southWest.getLng() + "," + vm.southWest.getLat() + ":"
+               + vm.northEast.getLng() + "," + vm.northEast.getLat()
+  }
+}
+
+export default Bounds

+ 63 - 0
src/utils/ol-map/Check.js

@@ -0,0 +1,63 @@
+import * as Info from './Info'
+class Check{
+  static lngLat(lng,lat){
+    let msg = ''
+    let result = false
+    if(lat == null || lng == null || lat == undefined || lng == undefined){
+      msg = Check.addHeader("经纬度不能为null")
+      return Check.message(msg,result)
+    }
+    if(Check.isNumber(lat) || Check.isNumber(lng)){
+      msg = Check.addHeader("经纬度应为数字")
+      return Check.message(msg,result)
+    }
+    if(lat<-90 || lat>90){
+      msg = Check.addHeader("纬度lat应该大于-90小于90")
+      return Check.message(msg,result)
+    }
+    if(lng<-180 || lng>180){
+      msg = Check.addHeader("经度lng应该大于-180小于180")
+      return  Check.message(msg,result)
+    }
+    if(msg == ''){
+      result = true
+    }
+    return Check.message(msg,result)
+  }
+
+  static isNumber(str){
+    
+    if(str == null || undefined){
+      return false
+    }
+    
+    if((typeof str=='string')&&str.constructor==String){
+      return false
+    }
+    
+    if(!isNaN(str)){
+      return false
+    }
+    return true
+  }
+  static notEmpty(name,str){
+    let result = false;
+    if(str == null || undefined){
+      result =  false
+    }
+    
+    if((typeof str=='string') && str.constructor==String && str !=''){
+      result =  true
+    }else{
+      result =  false
+    }
+    return Check.message(Check.addHeader(name+"必须为字符串且不能为空"),result);
+  }
+  static addHeader(str){
+    return Info.version+":"+str
+  }
+  static message(msg,isPass){
+    return {msg:msg,isPass:isPass}
+  }
+}
+export default Check

+ 143 - 0
src/utils/ol-map/Circle.js

@@ -0,0 +1,143 @@
+import Feature from 'ol/Feature'
+import Fill from 'ol/style/Fill'
+import Stroke from 'ol/style/Stroke'
+import Style from 'ol/style/Style'
+import * as olExtent from 'ol/extent'
+import * as proj from 'ol/proj'
+import OLCircle from 'ol/geom/Circle'
+import KBaseObject from './KBaseObject'
+import Common from './Common'
+
+/**
+ * @description KMap.Circle 圆标记
+ */
+class Circle extends KBaseObject{
+  /**
+   * @description KMap.Circle 构造函数
+   * @param {JSON Object} lng 必填 param.lat 必填 param
+   */
+  /**
+   * Creates an instance of Circle.
+   * @param {number} lng 经度 必填
+   * @param {number} lat 纬度 必填
+   * @param {number} radius 圆半径 必填
+   * @param {JSON} param param.storkeWidth圆外线宽度 选填;
+   * param.strokeColor圆外线颜色 选填;
+   * param.background 圆背景颜色 选填。
+   * @param {KMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+   * @memberof Circle
+   */
+  constructor(lng,lat,radius,param,mapInstance = null){
+    super(mapInstance)
+    const vm = this
+    Common.checkLngLat(lng,lat)
+    vm.lng = lng
+    vm.lat = lat
+    vm.radius = radius
+    //创建默认圆标记图层
+    vm.source = vm.mapInstance.polygonLayer.getSource()
+    //构造函数和对象参数
+    vm.style = vm.initStyle(param)
+    vm.circle = vm.initFeature()
+    vm.circle.setStyle(vm.style)
+    vm.source.addFeature(vm.circle)
+  }
+  /**
+   * @description 初始化圆样式 供内部使用
+   * @param {JSON} param param.storkeWidth圆外线宽度 选填;
+   * param.strokeColor圆外线颜色 选填;
+   * param.background 圆背景颜色 选填。
+   * @return {Style} 
+   * @memberof Circle
+  */
+  initStyle(param){
+    const vm = this
+    let background = (param != undefined && param.background != undefined )? param.background : "rgba(255,0,0,.5)"
+    let strokeWidth = (param != undefined && param.strokeWidth != undefined )? param.strokeWidth : 1
+    let strokeColor = (param != undefined && param.strokeColor != undefined )? param.strokeColor : "rgba(255,0,0,0)"
+    let style = new Style({
+      fill: new Fill({ //矢量图层填充颜色,以及透明度
+        color: background
+      }),
+      stroke: new Stroke({ //边界样式
+        color: strokeColor,
+        width: strokeWidth
+      })
+    })
+    return style
+  }
+  
+  /**
+   * @description 初始化Feature 内部函数
+   * @return {Feature} 
+   * @memberof Circle
+   */
+  initFeature(){
+    const vm = this
+    vm.center = proj.fromLonLat([vm.lng,vm.lat])
+    var circle = new OLCircle(vm.center, vm.radius,'XY')
+    let feature = new Feature({
+      geometry: circle
+    })
+    return feature
+  }
+  
+  /**
+   * @description 修改半径
+   * @param {number} newRadius 新的半径值
+   * @memberof Circle
+   */
+  setRadius(newRadius) {
+    const vm = this
+    vm.radius = newRadius
+    vm.circle.getGeometry().setRadius(newRadius)
+	}
+	
+  /**
+  * @description 显示圆标记
+  * @memberof Circle
+  */
+  show() {
+    const vm = this
+    if(!vm.source.hasFeature(vm.circle)){
+      vm.source.addFeature(vm.circle)
+    }
+	}
+
+	/**
+   * @description 隐藏圆标记
+   * @memberof Circle
+   */
+  hide() {
+    const vm = this
+    if(vm.source.hasFeature(vm.circle)){
+      vm.source.removeFeature(vm.circle)
+    }
+	}
+  
+	/**
+   * @description 删除圆标记
+   * @memberof Circle
+   */
+  remove() {
+    const vm = this
+    if(vm.source.hasFeature(vm.circle)){
+      vm.source.removeFeature(vm.circle)
+    }
+	}
+  
+  /**
+	 * 地图视角缩放到圆标记范围
+	 * @param duration 动画持续时间(单位:毫秒) 选填,默认0毫秒
+	 */
+	zoomToExtent(duration) {
+    const vm = this
+		duration = (duration)? duration : 0
+    let extentBound = vm.circle.getGeometry().extent_
+		vm.map.getView().fit(extentBound,{
+			duration: duration
+		})
+	}
+}
+
+export default Circle

+ 145 - 0
src/utils/ol-map/Common.js

@@ -0,0 +1,145 @@
+import Size from './Size'
+import Pixel from './Pixel'
+import LngLat from './LngLat'
+import Bounds from './Bounds'
+import Check from './Check'
+import * as olProj from 'ol/proj';
+/**
+ * @description KMap.Common类 通用静态方法
+ */
+class Common{
+	static ShowLevel = [1,22]
+	/**
+	 *@description 底图Zoom限制
+	*/
+	static BaseLayerZoom = [1,18]
+	/**
+	 * @description 利通地图像素转OpenLayers地图像素
+	 * @param {KMap.Pixel} pixel KMap.Pixel格式的像素,必填
+	 * @returns {Array} OpenLayers格式的像素,包含两个元素的数组[x,y]
+	*/
+	static KMapPixel2MapPixel(pixel){
+		let mapPixel = [pixel.getX(),pixel.getY()]
+		return mapPixel
+	}
+
+	/**
+	 * @description OpenLayers地图像素转利通地图像素
+	 * @param {Array} pixel OpenLayers格式的像素,包含两个元素的数组[x,y],必填
+	 * @returns {KMap.Pixel} KMap.Pixel格式的像素
+	*/
+	static MapPixel2KMapPixel(pixel) {
+		let ltPixel = new Pixel(pixel[0], pixel[1])
+		return ltPixel
+	}
+
+	/**
+	 * @description 利通地图像素尺寸转OpenLayers地图像素尺寸
+	 * @param {KMap.Size} size KMap.Size格式的尺寸,必填
+	 * @returns {Array} OpenLayers地图像素尺寸,包含两个元素的数组[width,height]
+	*/
+	static KMapSize2MapSize(size) {
+		let mapSize = [size.getWidth(), size.getHeight()]
+		return mapSize
+	}
+
+	/**
+	 * @description OpenLayers地图像素尺寸转利通地图像素尺寸
+	 * @param {Array} size OpenLayers地图像素尺寸,包含两个元素的数组[width,height],必填
+	 * @returns {KMap.Size} 格式的尺寸
+	*/
+	static MapSize2KMapSize(size) {
+		let ltSize = new Size(size[0], size[1])
+		return ltSize
+	}
+
+	/**
+	 * @description 利通地图经纬度转OpenLayers地图经纬度
+	 * @param {KMap.LngLat} lnglat KMap.LngLat格式的经纬度,必填
+	 * @returns {Array} OpenLayers的经纬度格式,包含两个元素的数组[lng,lat]
+	*/
+	static KMapLngLat2MapLngLat(lnglat) {
+		let alnglat = [lnglat.getLng(),lnglat.getLat()]
+		return alnglat
+	}
+
+	/**
+	 * @description OpenLayers地图经纬度转利通地图经纬度
+	 * @param {Array} lnglat OpenLayers的经纬度格式,包含两个元素的数组[lng,lat],必填
+	 * @returns {KMap.LngLat} KMap.LngLat格式的经纬度
+	*/
+	static MapLngLat2KMapLngLat(lnglat) {
+		let ltlnglat = new LngLat(lnglat[0], lnglat[1])
+		return ltlnglat
+	}
+
+	/**
+	 * @description 利通地图经纬度矩形范围转OpenLayers地图经纬度矩形范围
+	 * @param {KMap.Bounds} bounds KMap.Bounds对象,必填
+	 * @returns {Array} 西南角经度、西南角纬度、东北角经度、东北角纬度构成的数组
+	*/
+	static KMapBounds2MapBounds(bounds) {
+		let array = new Array()
+		let southWest = bounds.getSouthWest()
+		let northEast = bounds.getNorthEast()
+		array.push(southWest.getLng())
+		array.push(southWest.getLat())
+		array.push(northEast.getLng())
+		array.push(northEast.getLat())
+		return array
+	}
+
+	/**
+	 * @description OpenLayers地图经纬度矩形范围转利通地图经纬度范围
+	 * @param {Array} bounds 西南角经度、西南角纬度、东北角经度、东北角纬度构成的数组,必填
+	 * @returns {KMap.Bounds} KMap.Bounds类型对象
+	*/
+	static MapBounds2KMapBounds(bounds) {
+		let southWest = [bounds[0],bounds[1]]
+		let northEast = [bounds[2],bounds[3]]
+		southWest = new LngLat(southWest[0],southWest[1])
+		northEast = new LngLat(northEast[0],northEast[1])
+		bounds = new Bounds(southWest,northEast)
+		return bounds
+	}
+	static toWGS84LngLat(map,coordinate){
+		map.getCoor
+		return olProj.transform(coordinate,map.getProjection(),"EPSG:4326")
+	}
+
+	/**
+	 * @description 扩展JSON对象属性
+	 * @param {JSON} des 目标JSON对象,必填
+	 * @param {JSON} src 源JSON对象,必填
+	 * @param {boolean} override 是否覆盖属性,选填
+	 * @returns {JSON} 目标JSON对象
+	*/
+	static extend(des, src, override){
+		if(src instanceof Array){
+			for(let i = 0, len = src.length; i < len; i++)
+				Common.extend(des, src[i], override)
+		}
+		for( let i in src){
+			if(override || !(i in des)){
+				des[i] = src[i]
+			}
+		}
+		return des
+	}
+	static checkLngLat(lng,lat){
+		let info = Check.lngLat(lng,lat)
+    if(!info.isPass){
+      throw new Error(info.msg)
+    }
+	}
+
+	static notEmpty(name,str){
+		let info = Check.notEmpty(name,str)
+    if(!info.isPass){
+      throw new Error(info.msg)
+    }
+	}
+
+}
+
+export default Common

+ 37 - 0
src/utils/ol-map/CustomLayer.js

@@ -0,0 +1,37 @@
+import Tile from 'ol/layer/WebGLTile'
+import XYZ from 'ol/source/XYZ'
+import KBaseObject from './KBaseObject'
+/**
+ * @description KMap.CustomLayer 自定义离线切片图层类
+ */
+class CustomLayer extends KBaseObject{
+  /**
+   * @description 离线切片图层类
+   * @param {string} layerUrl 切片url地址
+  */
+  constructor(layerUrl,mapInstance = null){
+    super(mapInstance)
+    const vm = this
+    let layer = new Tile()
+    let source = new XYZ({
+      url : layerUrl
+    })
+    layer.setSource(source)
+    vm.layer = layer
+    vm.map.addLayer(vm.layer)
+  }
+  hide(){
+    const vm = this
+    vm.layer.setVisible(false)
+  }
+  show(){
+    const vm = this
+    vm.layer.setVisible(true)
+  }
+  remove(){
+    const vm = this
+    vm.map.removeLayer(vm.layer)
+  }
+}
+
+export default CustomLayer

+ 9 - 0
src/utils/ol-map/Enum.js

@@ -0,0 +1,9 @@
+/**
+ * @description KMap.LayerTypeEnum 底图类型枚举
+*/
+export const LayerTypeEnum = {
+  'ARCGISTile':"ARCGISTile",
+  'GaoDeTile':"GaoDeTile",
+  "WGS84Tile":"WGS84Tile",
+  "BaiDuTile":"BaiDuTile"
+}

+ 19 - 0
src/utils/ol-map/Info.js

@@ -0,0 +1,19 @@
+/**
+ * @module KMap/Info
+ * @description api基本信息
+ */
+
+/**
+ * @const
+ * @version
+ * @type {string}
+ * @description 版本
+ */
+export const version = "KMap 1.0 Base On OpenLayers6.14.1"
+
+/**
+ * @const
+ * @type {string}
+ * @description 原生引擎名称
+ */
+export const srcApiName = "OpenLayers6.14.1"

+ 191 - 0
src/utils/ol-map/InfoWindow.js

@@ -0,0 +1,191 @@
+import Common from './Common'
+import * as proj from 'ol/proj'
+import KBaseObject from './KBaseObject'
+/**
+ * @description KMap.InfoWindow 弹窗类
+ */
+class InfoWindow extends KBaseObject{
+	/**
+	 * Creates an instance of InfoWindow.
+	 * @param {*} param
+	 * @param {KMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof InfoWindow
+	 */
+	constructor(param,mapInstance = null){
+    let {content,position,offsetX,offsetY,type} = param
+		super(mapInstance)
+		const vm = this
+		if(!offsetX){
+			offsetX = 0
+		}
+		if(!offsetY){
+			offsetY = 0
+		}
+    this.initInfoWindow(content,position,offsetX,offsetY,type)
+  }
+
+  initInfoWindow(content,position,offsetX,offsetY,type){
+    const vm = this
+    if(type == "click") {
+      vm.infoWindow = vm.mapInstance.infoWindow_click
+      vm.infoWindowBox = document.getElementById("infowindow-click")
+    }
+    else if(type == "mousemove") {
+      vm.infoWindow = vm.mapInstance.infoWindow_move
+      vm.infoWindowBox = document.getElementById("infowindow-move")
+      vm.infoWindowBox.style.zIndex = 1
+    }
+    vm.infoWindowBox.style.position = "absolute"
+    //获取弹窗
+    vm.InfoWindow = vm.infoWindowBox
+    vm.content = content.outerHTML
+    vm.position = position
+    vm.offsetX = offsetX
+    vm.offsetY = offsetY
+  }
+
+  /**
+	 * 在地图指定位置打开弹窗
+	 * @param clearState 打开新弹窗是否关闭其他弹窗(默认true),选填
+	*/
+	open(clearState) {
+    const vm = this
+		clearState = (clearState!=undefined)? clearState : true
+		if(clearState) {
+			vm.mapInstance.infoWindow_click.setPosition(undefined)
+			vm.mapInstance.infoWindow_move.setPosition(undefined)
+		} //清空地图弹窗
+
+		if(vm.content == undefined || vm.content == ""){return}
+		vm.infoWindowBox.innerHTML = vm.content
+    let vmPosition = proj.fromLonLat(Common.KMapLngLat2MapLngLat(vm.position))
+
+		vm.infoWindow.setPosition(vmPosition)
+		var left = -(vm.infoWindowBox.offsetWidth)/2 + vm.offsetX
+		var top = -(vm.infoWindowBox.offsetHeight) + vm.offsetY
+		vm.infoWindowBox.style.left = left + "px"
+		vm.infoWindowBox.style.top = top + "px"
+
+		//设置全局弹窗范围
+		var pixel = vm.mapInstance.lngLatToContainer(vm.position)
+		vm.mapInstance.infoWindowPixel = new Array()
+		vm.mapInstance.infoWindowPixel[0] = pixel.getX() + left
+		vm.mapInstance.infoWindowPixel[1] = pixel.getY() + top
+		vm.mapInstance.infoWindowPixel[2] = pixel.getX() + left + vm.infoWindowBox.offsetWidth
+		vm.mapInstance.infoWindowPixel[3] = pixel.getY() + top + vm.infoWindowBox.offsetHeight
+		
+    //弹窗超出屏幕位置矫正
+		var mapCenter = vm.mapInstance.getCenter()
+    
+    //地图中心
+		mapCenter = vm.mapInstance.lngLatToContainer(mapCenter)
+    
+    //将利通地图像素转换成OL地图像素
+		mapCenter = Common.KMapPixel2MapPixel(mapCenter)
+		
+    //弹窗坐标初始坐标
+    pixel = vm.mapInstance.lngLatToContainer(vm.position)
+		
+    //将利通地图像素转换成OL地图像素
+    pixel = Common.KMapPixel2MapPixel(pixel)
+		left = -(vm.infoWindowBox.offsetWidth)/2 + vm.offsetX
+		top = -(vm.infoWindowBox.offsetHeight) + vm.offsetY
+    
+    //弹窗左上角坐标
+		let infoWindow_left = [pixel[0]+left, pixel[1]+top]
+    
+    //弹窗右上角坐标
+		let infoWindow_right = [infoWindow_left[0]+vm.infoWindowBox.offsetWidth, pixel[1]+top]
+		if(infoWindow_left[1] < 0) {
+			mapCenter[1] -= -(infoWindow_left[1])
+			vm.mapInstance.infoWindowPixel[1] -= infoWindow_left[1]
+			vm.mapInstance.infoWindowPixel[3] -= infoWindow_left[1]
+		}
+
+		if(infoWindow_left[0] < 0) {
+			mapCenter[0] -= -(infoWindow_left[0]);
+			vm.mapInstance.infoWindowPixel[0] -= infoWindow_left[0]
+			vm.mapInstance.infoWindowPixel[2] -= infoWindow_left[0]
+		}
+    
+    //容器宽度
+		var containerWidth = vm.mapInstance.getTarget().offsetWidth
+		if(infoWindow_left[0] > 0 && infoWindow_right[0] > containerWidth) {
+			mapCenter[0] += (infoWindow_right[0]-containerWidth)
+		}
+    
+    //将OL地图像素转换成利通地图像素
+		mapCenter = Common.MapPixel2KMapPixel(mapCenter)
+		mapCenter = vm.mapInstance.containerToLngLat(mapCenter)
+		vm.mapInstance.panTo(mapCenter)
+	}
+
+	/**
+	 *关闭弹窗
+	*/
+	close() {
+    const vm = this
+		vm.infoWindow.setPosition(undefined)
+		vm.mapInstance.infoWindowPixel = null
+	}
+
+	/**
+	 *获取弹窗是否打开--暂无该方法
+	*/
+	getIsOpen() {}
+
+	/**
+	 * 获取弹窗内容
+	 * @returns 弹窗内容
+	*/
+	getContent() {
+    const vm = this
+		let content = vm.infoWindowBox.innerHTML
+		return content
+	}
+
+	/**
+	 * 设置弹窗内容--暂无该方法
+	*/
+	setContent(content) {
+    const vm = this
+		vm.infoWindowBox.innerHTML = content.outerHTML
+	}
+
+	/**
+	 * 获取弹窗坐标
+	 * @returns KMap.LngLat格式的弹窗坐标
+	*/
+	getPosition() {
+    const vm = this
+		let position = vm.infoWindow.getPosition()
+		position = proj.toLonLat(position)
+		return Common.MapLngLat2KMapLngLat(position)
+	}
+
+	/**
+	* 设置弹窗坐标--暂无该方法
+	*/
+	setPosition(position) {
+    const vm = this
+		vm.infoWindow.setPosition(proj.fromLonLat(Common.KMapLngLat2MapLngLat(position)))
+	}
+
+	/**
+	* 获取弹窗大小
+	* @returns 弹窗大小数组,[width,height]
+	*/
+	getSize() {
+    const vm = this
+		let size = {}
+		size.width = (vm.infoWindowBox.style.width)? vm.infoWindowBox.style.width : "auto"
+		size.height = (vm.infoWindowBox.style.height)? vm.infoWindowBox.style.height : "auto"
+		return size
+	}
+
+	/**
+	* 设置弹窗大小--暂无该方法
+	*/
+	setSize(width,height) {}
+}
+export default InfoWindow

+ 11 - 0
src/utils/ol-map/KBaseObject.js

@@ -0,0 +1,11 @@
+import Map from  './Map'
+class KBaseObject{
+  constructor(mapInstance){
+    const vm = this
+		//利通map实例
+		vm.mapInstance = mapInstance || Map.Instance
+		//获取ol map对象
+		vm.map = vm.mapInstance.map
+  }
+}
+export default KBaseObject

+ 38 - 0
src/utils/ol-map/KMap.js

@@ -0,0 +1,38 @@
+import * as Info from './Info'
+import Map from './Map'
+import Common from './Common'
+import Bounds from './Bounds'
+import Marker from './Marker'
+import Pixel from './Pixel'
+import Size from './Size'
+import LngLat from './LngLat'
+import InfoWindow from './InfoWindow'
+import Polyline from './Polyline'
+import Polygon from './Polygon'
+import Circle from './Circle'
+import CustomLayer from './CustomLayer'
+import WMTSLayer from './WMTSLayer'
+import VectorLayer from './VectorLayer'
+import XYZLayer from './XYZLayer'
+import VTLayer from './VTLayer'
+import VectorStyle from './VectorStyle'
+export {
+  Info,
+  Map,
+  Common,
+  Bounds,
+  Marker,
+  Polyline,
+  Polygon,
+  Circle,
+  Pixel,
+  Size,
+  LngLat,
+  InfoWindow,
+  CustomLayer,
+  WMTSLayer,
+  XYZLayer,
+  VectorLayer,
+  VTLayer,
+  VectorStyle,
+}

+ 313 - 0
src/utils/ol-map/Label.js

@@ -0,0 +1,313 @@
+import Common from './Common'
+import Feature from 'ol/Feature'
+import Point from 'ol/geom/Point'
+import Style from 'ol/style/Style'
+import Fill from 'ol/style/Fill'
+import Stroke from 'ol/style/Stroke'
+import Text from 'ol/style/Text'
+import KBaseObject from './KBaseObject'
+import * as proj from 'ol/proj'
+/**
+ * @description KMap.Label 文本标记类
+*/
+class Label extends KBaseObject{
+	/**
+	 * Creates an instance of Label.
+	 * @param {*} param
+	 * @param {KMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof Label
+	 */
+	constructor(param,options,mapInstance = null){
+		super(mapInstance)
+		const vm = this
+    let lng = (param.lng != undefined)? Number(param.lng) : vm.mapInstance.getCenter()[0]
+    let lat = (param.lat != undefined)? Number(param.lat) : vm.mapInstance.getCenter()[1]
+    let text = (param.text != undefined)? param.text : ""
+    let font = (param.font != undefined)? param.font : '13px sans-serif'
+    let offsetX = (param.offsetX != undefined)? param.offsetX : 0
+    let offsetY = (param.offsetY != undefined)? param.offsetY : 0
+    let rotation = (param.rotation != undefined)? param.rotation : 0
+    let fill = (param.fill != undefined)? param.fill : "black"
+    let backgroundColor = (param.backgroundColor != undefined)? param.backgroundColor : "#fff"
+    let backgroundStroke = (param.backgroundStroke != undefined)? param.backgroundStroke : "black"
+    let padding = (param.padding != undefined)? param.padding : [0,5,0,5]
+    Common.checkLngLat(lng,lat)
+		//创建文本标记
+    vm.label = new Feature({
+      geometry: new Point(proj.fromLonLat([lng,lat])),
+      properties: null
+    })
+    vm.style = new Style({
+      text: new Text({
+        text: text,
+        font: font,
+        offsetX: offsetX,
+        offsetY: offsetY,
+        rotateWithView: false,//文本是否可旋转
+        rotation: rotation,
+        fill: new Fill({
+            color: fill
+        }),
+        backgroundFill: new Fill({
+            color: backgroundColor
+        }),
+        backgroundStroke: new Stroke({
+            color: backgroundStroke
+        }),
+        padding: padding
+      }),
+      zIndex: 0
+    })
+    vm.label.setStyle(vm.style)
+    vm.text = vm.style.getText() //文本标记内容
+    vm.point = vm.label.getGeometry() //文本标记对象
+    vm.source = vm.mapInstance.labelLayer.getSource() //地图文本标记图层
+	
+	if(options && options.source){
+		vm.source = source;
+	}
+	//文本标记添加到地图
+    vm.source.addFeature(vm.label)
+  }
+
+  /**
+	 * 获取id
+	 * @returns id
+	*/
+	getId() {
+		const vm = this
+		let id = vm.label.getId()
+		return id
+	}
+
+	/**
+	 * 设置id
+	 * @param id 必填
+	 */
+	setId(id) {
+		const vm = this
+		vm.label.setId(id)
+	}
+
+	/**
+	 * 显示文本标记
+	 */
+	show() {
+		const vm = this
+		if(!vm.source.hasFeature(label)) {
+			vm.source.addFeature(label)
+		}
+	}
+
+	/**
+	 * 隐藏文本标记
+	*/
+	hide() {
+		const vm = this
+		if(vm.source.hasFeature(label)) {
+			vm.source.removeFeature(label)
+		}
+	}
+
+	/**
+	 * 删除文本标记
+	 */
+	remove() {
+		const vm = this
+		if(vm.source.hasFeature(label)) {
+			vm.source.removeFeature(label)
+		}
+	}
+
+	/**
+	 * 获取文本标记坐标
+	 * @param 返回KMap.LngLat格式的经纬度
+	*/
+	getPosition() {
+		const vm = this
+		let position = new proj.toLonLat(vm.point.getCoordinates())
+		position = Common.MapLngLat2KMapLngLat(position)
+		return position
+	}
+
+	/**
+	 * 设置文本标记坐标
+	 * @param KMap.LngLat格式的文本标记经纬度坐标,必填
+	*/
+	setPosition(lnglat){
+		lnglat = Common.KMapLngLat2MapLngLat(lnglat)
+		let position = proj.fromLonLat(lnglat)
+		point.setCoordinates(position)
+	}
+
+	/**
+	 * 获取文本标记缩放值
+	 * @returns 文本标记缩放值
+	 */
+	getScale(){
+		const vm = this
+		let scale = vm.text.getScale()
+		return scale
+	}
+
+	/**
+	 * 设置文本标记缩放值
+	 * @param scale 缩放值,必填
+	*/
+	setScale(scale) {
+		const vm = this
+		vm.text.setScale(scale)
+	}
+
+	/**
+	 * 获取文本标记X轴方向偏移量
+	 * @returns X轴方向偏移量
+	*/
+	getOffsetX(){
+		const vm = this
+		let offsetX = vm.text.offsetX_
+		return offsetX
+	}
+
+	/**
+	 * 设置文本标记X轴方向偏移
+	 * @param X轴方向偏移(正向右,负向左),必填
+	*/
+	setOffsetX(offsetX) {
+		const vm = this
+		vm.text.offsetX_ = offsetX
+	}
+
+	/**
+	 * 获取文本标记Y轴方向偏移量
+	 * @returns Y轴方向偏移量
+	*/
+	getOffsetY() {
+		const vm = this
+		let offsetY = vm.text.offsetY_
+		return offsetY
+	}
+
+	/**
+	 * 设置文本标记Y轴方向偏移
+	 * @param offsetY Y轴方向偏移(正向下,负向上),必填
+	*/
+	setOffsetY(offsetY) {
+		const vm = this
+		vm.text.offsetY_ = offsetY
+	}
+
+	/**
+	 * 获取文本标记旋转弧度
+	 * @returns 文本标记旋转弧度
+	*/
+	getAngle(){
+		const vm = this
+		let rotation = vm.text.rotation_
+		return rotation
+	}
+
+	/**
+	 * 设置文本标记旋转弧度
+	 * @param rotation 文本标记旋转弧度,必填
+	*/
+	setAngle(rotation){
+		const vm = this
+		vm.text.rotation_ = rotation
+	}
+
+	/**
+	 * 获取文本标记叠加顺序
+	 * @returns 文本标记叠加顺序
+	*/
+	getZIndex() {
+		const vm = this
+		let index = vm.style.getZIndex()
+		return index
+	}
+
+	
+  /**
+	 * 设置文本标记叠加顺序
+	 * @param index index值较大的点标记显示在上方 ,值相同时后创建的点标记显示在上方,必填
+	*/
+	setZIndex(index) {
+		const vm = this
+		vm.style.setZIndex(index)
+	}
+	
+  /**
+	 * 获取用户自定义属性
+	 * @returns 用户自定义属性(支持数字,字符串,json)
+	*/
+	getExtData(){
+		const vm = this
+		let extData = vm.label.values_.properties
+		return extData
+	}
+	
+  /**
+	 * 设置用户自定义属性
+	 * @param extData 用户自定义属性(支持数字,字符串,json),必填
+	*/
+	setExtData(extData){
+		const vm = this
+		extData = (extData)? extData :null
+		if(extData != null) {
+			vm.label.values_.properties = extData
+		}
+	}
+
+	/**
+	 * 注册文本标记事件
+	 * @param eventName 鼠标事件
+	 * "click":鼠标点击事件 "mousemove":鼠标悬停事件
+	 * @param callback 选中文本标记时触发函数
+	*/
+	on(eventName,callback) {
+    const vm = this
+		vm.label.on(eventName,function(e) {
+			if(eventName == "mousemove"){
+				if(vm.label.ol_uid != vm.mapInstance.InfowindowmoveUID)
+				{
+					vm.mapInstance.InfowindowmoveUID = vm.label.ol_uid
+					callback(e)
+				}
+			}
+			else{
+				callback(e)
+			}
+		})
+	}
+	
+  /**
+	 * 注销文本标记事件(暂无该方法)
+	*/
+	off(eventName,handler) {}
+	
+  /**
+	 * 获取文本标记地图对象
+	 * @returns 地图对象
+	*/
+	getMap() {
+    const vm = this
+		let map = vm.map
+		return map
+	}
+
+	/**
+	 * 设置文本标记地图对象,传入null时,移除点标记,
+	 * 建议不使用此API,使用remove方法
+	 * @param map 传入null,移除点标记
+	*/
+	setMap(map) {
+    const vm = this
+		map = (vm.map)? vm.map : null
+		if(map == null) {
+			if(vm.source.hasFeature(vm.abel)) {
+				vm.source.removeFeature(vm.label)
+			}
+		}
+	}
+}
+export default Label

+ 78 - 0
src/utils/ol-map/LngLat.js

@@ -0,0 +1,78 @@
+import Common from "./Common"
+/**
+ * @description KMap.LngLat 经纬度
+*/
+class LngLat{
+  /**
+   * @param {number} lng 纬度
+   * @param {number} lat 经度
+   * @constructor
+   */
+  constructor(lng,lat){
+    Common.checkLngLat(lng,lat)
+    let maplnglat = [Number(lng),Number(lat)]
+    this.lngLat = maplnglat
+  }
+
+
+	/**
+	 * @description 当前经纬度坐标值经度移动w,纬度移动s,得到新的坐标。 经度向右移为正值,纬度向上移为正值,单位为°
+	 * @param {number} w 经度移动量
+	 * @param {number} s 纬度移动量
+	 */
+  offset(w, s) {
+    let lng = this.lngLat[0]+w
+    let lat = this.lngLat[1]+s
+    return new LngLat(lng,lat)
+  }
+
+  /**
+   * @description 当前经纬度和传入经纬度之间的地面距离,单位为米----暂无该方法
+   * @param {number} lnglat 经纬度
+   */
+  distance(lnglat) {
+    return null
+  }
+
+  /**
+   * @description 获取经度
+   * @returns {number} 返回经度
+   */
+	getLng() {
+		let lng = this.lngLat[0]
+		return lng
+	}
+
+	/**
+	 * @description 获取纬度
+	 * @returns {number} 返回纬度
+	 */
+	getLat() {
+		let lat = this.lngLat[1]
+		return lat
+	}
+
+	/**
+	 * @description 判断当前坐标对象与传入坐标对象是否相等
+	 * @param {KMap.LngLat} lnglat 格式的经纬度,必填
+	 * @returns {boolean} 坐标相等返回true,坐标不相等返回false
+	 */
+  equals(lnglat) {
+    if(lnglat.getLng() == this.lngLat[0] && lnglat.getLat() == this.lngLat[1]){
+      return true
+    }
+    else{
+      return false
+    }
+  }
+
+  /**
+   * @description LngLat对象以字符串的形式返回。
+   * @returns {String} 返回经纬度格式的字符串,用逗号连接
+   */
+  toString() { 
+    return this.lngLat[0] + "," + this.lngLat[1]
+  }
+}
+
+export default LngLat

+ 1318 - 0
src/utils/ol-map/Map.js

@@ -0,0 +1,1318 @@
+import OLMap from 'ol/Map'
+import View from 'ol/View'
+import * as proj from 'ol/proj'
+import * as interaction from 'ol/interaction'
+import { Draw, Modify } from 'ol/interaction'
+import 'ol/ol.css'
+import './css/KMap.css'
+import * as Enum from './Enum'
+import Common from './Common'
+import VectorLayer from './VectorLayer'
+import * as Extent from 'ol/extent'
+import Overlay from 'ol/Overlay'
+
+import { GeoJSON, WKT } from 'ol/format'
+import WMTSLayer from './WMTSLayer'
+import XYZLayer from './XYZLayer'
+import { Circle, Fill, Stroke } from 'ol/style.js';
+import { LineString, Point } from 'ol/geom';
+import { Style, Text } from 'ol/style';
+import { Feature } from "ol";
+import { getArea } from "ol/sphere"
+import {getCfg} from "@/api/modules/system"
+
+/**
+ * @description KMap.Map 地图类
+*/
+class Map {
+	/**
+	 * @description 地图实例
+	 * @static
+	 * @memberof Map
+	 */
+	// Instance = null
+	/**
+	 * @description 信息窗体像素对象
+	 * @memberof Map
+	 */
+	// infoWindowPixel = null
+	/**
+	 * @description 鼠标移动事件弹窗UID
+	 * @memberof Map
+	 */
+	// InfowindowmoveUID = -1
+	/**
+	 * @description 鼠标点击事件弹窗UID
+	 * @memberof Map
+	 */
+	// InfowindowclickUID = -1
+	/**
+	 * @description 是否有鼠标移动弹窗
+	 * @memberof Map
+	 */
+	// ClearMouseMoveInfoWindow = false
+	/**
+	 * @description X方向缩放
+	 * @memberof Map
+	 */
+	// scaleX = 1
+	/**
+	 * @description Y方向缩放
+	 * @memberof Map
+	 */
+	// scaleY = 1
+	/**
+	 * @param {string} id DOM元素ID
+	 * @param {number} zoomLevel 地图层级
+	 * @param {number} lng 纬度
+	 * @param {number} lat 经度
+	 * @description Map初始化方法
+	   * @constructor
+	*/
+	constructor(id, zoomLevel, lng, lat, projection, minZoom, maxZoom, mapType, dragPan = true, mouseWheelZoom = true) {
+		this.mapType = mapType || "img";
+		if (Map.Instance) {
+			Map.Instance = false;
+		}
+		if (projection) {
+			projection = proj.get(projection)
+		}
+		projection = projection || proj.get("EPSG:4326");
+		const vm = this
+		Map.Instance = this
+		let lnglat = [lng, lat]
+
+		if (projection.getCode() == "EPSG:3857") {
+			lnglat = proj.fromLonLat(lnglat);
+		}
+
+		Common.checkLngLat(lng, lat)
+		let view = new View({
+			center: lnglat,
+			zoom: zoomLevel,
+			minZoom: minZoom || Common.ShowLevel[0],
+			maxZoom: maxZoom || Common.ShowLevel[1],
+			projection: projection,
+			enableRotation: false
+		})
+		this.view = view
+		this.map = new OLMap({
+			interactions: interaction.defaults({
+				dragPan,
+				mouseWheelZoom
+			}).extend([
+				new interaction.DragRotateAndZoom()]),
+			target: id,
+			layers: [],//vm.baseLayer
+			view: view,
+			control: []
+		})
+		this.initBaseLayer(projection)
+		//初始化业务图层
+		this.initBusinessLayer()
+		//初始化地图信息弹窗
+		this.initInfoWindow()
+		//初始化地图基础事件
+		this.initMapBaseEvent()
+	}
+	/**
+	   * 初始化地图底图图层
+	 * @return {array}
+	   * @memberof Map
+	*/
+	async initBaseLayer(projection) {
+		if (this.mapType == "img") {
+			const img_wmts = await getCfg({ "k": "img_wmts_mkt", "resultType": "json" });
+			const cva_wmts = await getCfg({ "k": "cva_wmts_mkt", "resultType": "json" });
+			this.tdtImgLayer = new WMTSLayer(img_wmts.data, projection, this);
+			this.cva_torLayer = new WMTSLayer(cva_wmts.data, projection, this);
+		} else {
+			const img_wmts = await getCfg({ "k": "vec_c_wmts", "resultType": "json" });
+			const cva_wmts = await getCfg({ "k": "cva_c_wmts", "resultType": "json" });
+			this.tdtImgLayer = new WMTSLayer(img_wmts.data, projection, this);
+			this.cva_torLayer = new WMTSLayer(cva_wmts.data, projection, this);
+		}
+	}
+	addXYZLayer(url, options, zIndex) {
+		let xyz = new XYZLayer(url, options, zIndex || 3);
+		return xyz;
+	}
+	/**
+	 * @description 初始化业务图层
+	 * @memberof Map
+	 */
+	initBusinessLayer() {
+		const vm = this
+		let map = vm.map
+		//创建默认点标记图层
+		vm.markerLayer = new VectorLayer("defaultMarkerLayer", 101)
+		//创建默认线标记图层
+		vm.polyLineLayer = new VectorLayer("defaultPolylineLayer", 101)
+		//创建默认面图层
+		vm.polygonLayer = new VectorLayer("defaultPolygonLayer", 1000, {
+			style: vm.polygonStyle
+		})
+		//创建文本标记图层
+		vm.labelLayer = new VectorLayer("defaultLabelLayer", 99)
+
+		map.addLayer(vm.polygonLayer.layer)
+
+		map.once('postrender', function (event) {
+			map.addLayer(vm.markerLayer.layer)
+			map.addLayer(vm.polyLineLayer.layer)
+			map.addLayer(vm.labelLayer.layer)
+			map.on('click', function (evt) {
+				let coordinate = evt.coordinate;
+				// let newPoints = proj.transform(coordinate, 'EPSG:3857', 'EPSG:4326');
+				// debugger;
+			})
+			// map.addLayer(layer)
+		})
+	}
+
+	initDraw(callback) {
+		const vm = this
+		this.draw = new Draw({
+			type: 'MultiPolygon',
+			source: this.polygonLayer.source,
+			free: true,
+			style: vm.drawStyleFunc
+		})
+		this.draw.setActive(false)
+		this.map.addInteraction(this.draw);
+		this.draw.on("drawend", callback)
+	}
+	
+	startDraw() {
+		this.draw.setActive(true)
+	}
+
+	endDraw(){
+		this.draw.setActive(false)
+	}
+
+	modifyDraw(callback) {
+		this.modify = new Modify({
+			source: this.polygonLayer.source,
+			pixelTolerance: 10, //设置吸附像素值
+		})
+		this.map.addInteraction(this.modify);
+		this.modify.on("modifyend", callback)
+	}
+
+	startModify(){
+		this.modify.setActive(true)
+	}
+
+	endModify(){
+		this.modify.setActive(false)
+	}
+
+	drawStyleFunc(feature) {
+		const styles = [];
+		const type = feature.getGeometry().getType();
+		const coord = feature.getGeometry().getCoordinates();
+		for (let i = 0; i < coord.length - 1; i++) {
+			if (i % 2) {
+				styles.push(
+					new Style({
+						geometry: new Point(coord[i]),
+						image: new Circle({
+							radius: 6,
+							fill: new Fill({
+								color: '#2199F8'
+							}),
+							stroke: new Stroke({
+								color: '#fff',
+								width: 1
+							})
+						})
+					})
+				);
+			} else {
+				styles.push(
+					new Style({
+						geometry: new Point(coord[i]),
+						image: new Circle({
+							radius: 6,
+							fill: new Fill({
+								color: '#fff'
+							}),
+							stroke: new Stroke({
+								color: '#2199F8',
+								width: 1
+							})
+						})
+					})
+				);
+			}
+		}
+		if (type === 'LineString') {
+			for (let i = 0; i < coord.length - 1; i++) {
+				styles.push(
+					new Style({
+						geometry: new LineString([coord[i], coord[i + 1]]),
+						stroke: new Stroke({
+							color: '#2199F8',
+							width: 2
+						})
+					})
+				);
+			}
+		}
+		return styles;
+	}
+
+	polygonStyle(feature) {
+		const styles = [];
+		let fillStyle = {}
+		const coord = feature.getGeometry().getCoordinates();
+		if(feature.get("icon")==="point-act"){
+			for (let i = 0; i < coord[0].length - 1; i++) {
+				if (i % 2) {
+					styles.push(
+						new Style({
+							geometry: new Point(coord[0][i]),
+							image: new Circle({
+								radius: 6,
+								fill: new Fill({
+									color: '#2199F8'
+								}),
+								stroke: new Stroke({
+									color: '#fff',
+									width: 1
+								})
+							})
+						})
+					);
+				} else {
+					styles.push(
+						new Style({
+							geometry: new Point(coord[0][i]),
+							image: new Circle({
+								radius: 6,
+								fill: new Fill({
+									color: '#fff'
+								}),
+								stroke: new Stroke({
+									color: '#2199F8',
+									width: 1
+								})
+							})
+						})
+					);
+				}
+				fillStyle = new Style({
+					fill: new Fill({
+						color: [33,153, 248, 0.5]
+					}),
+					stroke: new Stroke({
+						color: '#2199F8',
+						width: 2
+					})
+				})
+			}
+		}else{
+			fillStyle = new Style({
+				fill: new Fill({
+					color: [33,153, 248, 0.5]
+				}),
+				stroke: new Stroke({
+					color: '#2199F8',
+					width: 2
+				})
+			})
+		}
+		let geom = feature.getGeometry().clone()
+		geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"))
+		let area = getArea(geom)
+		area = (area + area / 2) / 1000;
+		let areaValStyle = new Style({
+			text: new Text({
+				font: "16px sans-serif",
+				text: area.toFixed(2) + "亩",
+				// offsetX: 28,
+				// offsetY: -100,
+				fill: new Fill({ color: "#fff" }), // 字体颜色
+			}),
+		})
+		styles.push(fillStyle, areaValStyle);
+		return styles;
+	}
+
+	getLayerFeatures() {
+		const vm = this
+		let features = vm.polygonLayer.source.getFeatures()
+		return features
+	}
+
+	// 传入geojson,回显到polygon
+	setLayerPolygon(geometry) {
+		const vm = this
+		vm.polygonLayer.source.addFeatures(new GeoJSON().readFeatures(geometry))
+	}
+
+	setLayerWkt(wkt) {
+		const vm = this
+		let f = new Feature({ geometry: new WKT().readGeometry(wkt) })
+		const extent = f.getGeometry().getExtent()
+		vm.polygonLayer.source.addFeature(f)
+		vm.map.getView().fit(extent, { padding: [20, 20, 20, 20] });
+	}
+
+	addLayer(layer) {
+		const vm = this
+		vm.map.addLayer(layer)
+	}
+	removeLayer(layer) {
+		const vm = this
+		vm.map.removeLayer(layer)
+	}
+	/**
+	 * @description 初始化信息弹窗
+	 * @memberof Map
+	 */
+	initInfoWindow() {
+		const vm = this
+		//创建地图弹窗容器
+		let infoWindowBoxClick = document.createElement("div")
+		let infoWindowBoxMove = document.createElement("div")
+		let mapTarget = vm.map.getTargetElement()
+		infoWindowBoxClick.id = "infowindow-click"
+		infoWindowBoxMove.id = "infowindow-move"
+		infoWindowBoxClick.style.zIndex = 999
+		infoWindowBoxMove.style.zIndex = 999
+		mapTarget.appendChild(infoWindowBoxClick)
+		mapTarget.appendChild(infoWindowBoxMove)
+		vm.infoWindow_click = new Overlay({
+			element: infoWindowBoxClick
+		})
+		vm.infoWindow_move = new Overlay({
+			element: infoWindowBoxMove
+		})
+		//添加点击弹窗
+		vm.map.addOverlay(vm.infoWindow_click)
+		//添加悬停弹窗
+		vm.map.addOverlay(vm.infoWindow_move)
+	}
+	/**
+	 * @description 初始化地图基础事件
+	 * @memberof Map
+	 */
+	initMapBaseEvent() {
+		const vm = this
+		var allowTriggerEvent = function (pixel) {
+			var infoWindowPixel = vm.infoWindowPixel
+			if (infoWindowPixel == null) {
+				return true
+			}
+			var x = pixel[0]
+			var y = pixel[1]
+			if (x >= infoWindowPixel[0] && x <= infoWindowPixel[2] &&
+				y >= infoWindowPixel[1] && y <= infoWindowPixel[3]) {
+				return false
+			}
+			return true
+		}
+		vm.map.on('click', function (event) {
+			event.pixel[0] = (event.pixel[0] / vm.scaleX)
+			event.pixel[1] = (event.pixel[1] / vm.scaleY)
+			var clickFeature = vm.map.forEachFeatureAtPixel(event.pixel, function (feature) {
+				if (!allowTriggerEvent(event.pixel)) return
+				// 为点击到的feature发送自定义的click消息
+				if (feature.dispatchEvent != undefined) {
+					feature.dispatchEvent({ type: 'click', event: event })
+				}
+				return feature
+			})
+			//点击在地图空白处时清空弹窗
+			if (clickFeature == undefined) {
+				vm.clearInfoWindow()
+			}
+		})
+
+		//为地图注册鼠标点击事件的监听
+		vm.map.on('pointermove', function (event) {
+			event.pixel[0] = (event.pixel[0] / vm.scaleX)
+			event.pixel[1] = (event.pixel[1] / vm.scaleY)
+			var mousemoveFeature = vm.map.forEachFeatureAtPixel(event.pixel, function (feature) {
+				if (!allowTriggerEvent(event.pixel)) {
+					return
+				}
+				// 为点击到的feature发送自定义的mousemove消息
+				if (feature.dispatchEvent != undefined) {
+					feature.dispatchEvent({ type: 'mousemove', event: event })
+				}
+				return feature
+			})
+			//悬停在地图空白处时清空悬停弹窗
+			if (mousemoveFeature == undefined) {
+				vm.clearMouseMoveInfoWindow()
+			}
+			//设置鼠标悬停到覆盖物上的样式
+			var mapContainer = vm.getTarget()
+			if (mousemoveFeature) {
+				mapContainer.style.cursor = "pointer"
+			}
+			else {
+				mapContainer.style.cursor = "default"
+			}
+		})
+	}
+
+	setScale(x, y) {
+		const vm = this
+		// var mapContainer = vm.getTarget()
+		// mapContainer.style.overflow = 'hidden'
+		// var mapContent = mapContainer.getElementsByClassName('ol-viewport')[0]
+		// var scaleX = 1 / Number(x);
+		// var scaleY = 1 / Number(y);
+		vm.scaleX = Number(x)
+		vm.scaleY = Number(y)
+		// mapContent.style.transform = "scale("+scaleX+","+scaleY+")"
+	}
+
+	/**
+	 * @description 清除鼠标点击弹窗
+	 * @memberof Map
+	*/
+	clearInfoWindow() {
+		const vm = this
+		vm.infoWindow_click.setPosition(undefined)
+		vm.infoWindow_move.setPosition(undefined)
+		vm.infoWindowPixel = null
+		vm.InfowindowmoveUID = -1
+		vm.InfowindowclickUID = -1
+	}
+
+	/**
+	 * @description 清除鼠标移动弹窗
+	 * @memberof Map
+	*/
+	clearMouseMoveInfoWindow() {
+		const vm = this
+		if (vm.ClearMouseMoveInfoWindow) {
+			vm.infoWindow_move.setPosition(undefined)
+			vm.infoWindowPixel = null
+			vm.InfowindowmoveUID = -1
+		}
+	}
+
+	/**
+	   * @description 获取地图容器div
+	   * @returns 地图容器div
+	   * @memberof Map
+	   */
+	getTarget() {
+		let target = this.map.getTargetElement()
+		return target
+	}
+
+	/**
+	 * @description 获取地图容器尺寸
+	 * @returns KMap.Size格式的尺寸
+	 * @memberof Map
+	 */
+	getSize() {
+		let size = this.map.getSize()
+		console.log(Common)
+		size = Common.MapSize2KMapSize(size)
+		return size
+	}
+
+	/**
+	 * @description 获取地图投影EPSG类型
+	 * @returns 地图投影类型
+	 * @memberof Map
+	 */
+	getProjection() {
+		let projection = this.view.getProjection()
+		return projection
+	}
+
+	/**
+	 * @description 获取地图中心
+	 * @returns 地图中心,KMap.LngLat对象格式
+	 * @memberof Map
+	 */
+	getCenter() {
+		let center = this.view.getCenter()
+		center = proj.toLonLat(center)
+		return Common.MapLngLat2KMapLngLat(center)
+	}
+	/**
+	 * @description 获取地图中心
+	 * @returns 地图中心,KMap.LngLat对象格式
+	 * @memberof Map
+	 */
+	getCenter2() {
+		let center = this.view.getCenter()
+		center = proj.toLonLat(center)
+		return center
+	}
+
+	/**
+	 * @description 设置地图中心
+	 * @param {KMap.LngLat} position 地图中心位置,KMap.LngLat对象格式,必填
+	 * @memberof Map
+	 */
+	setCenter(position) {
+		let centerlnglat = Common.KMapLngLat2MapLngLat(position)
+		let center = proj.fromLonLat(centerlnglat)
+		this.view.setCenter(center)
+	}
+	getView() {
+		return this.view;
+	}
+	/**
+ * @description 设置地图中心
+ * @param {KMap.LngLat} position 地图中心位置,KMap.LngLat对象格式,必填
+ * @memberof Map
+  */
+	setCenter2(position) {
+		this.view.setCenter(position)
+	}
+	fitToView(center, zoom, dera) {
+		this.view.fit(center, { duration: dera })
+	}
+	/**
+	 * @description 地图中心点平移至指定点位置
+	 * @param {KMap.LngLat} point 指定点经纬度坐标,KMap.LngLat对象格式,必填
+	 * @param {number} zoom 缩放级别,选填参数,不填则使用当前缩放级别
+	 * @memberof Map
+	 */
+	panTo(point, zoom) {
+		point = Common.KMapLngLat2MapLngLat(point)
+		let center = proj.fromLonLat(point)
+		if (zoom) {
+			this.view.animate({ center: center }, { zoom: zoom })
+		}
+		else {
+			this.view.animate({ center: center })
+		}
+	}
+
+	/**
+	 * @description 地图放大一级显示
+	 * @memberof Map
+	 */
+	zoomIn() {
+		this.view.setZoom(this.getZoom() + 1)
+		return this.getZoom()
+	}
+
+	/**
+	 * @description 地图缩小一级显示
+	 * @memberof Map
+	 */
+	zoomOut() {
+		this.view.setZoom(this.getZoom() - 1)
+		return this.getZoom()
+	}
+
+	/**
+	 * @description 缩放到点标记图层范围
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	zoomToMarkerLayer(duration) {
+		const vm = this
+		duration = (duration != undefined) ? duration : 0
+		let markers = vm.markerLayer.getSource().getFeatures()
+		let coordinateArray = new Array()
+		for (let i = 0; i < markers.length; i++) {
+			coordinateArray.push(markers[i].getGeometry().getCoordinates())
+		}
+		let extentBound = new Extent.boundingExtent(coordinateArray)
+		this.view.fit(extentBound, {
+			duration: duration
+		})
+
+		this.view.fit(vm.markerLayer.getSource().getExtent(), {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 缩放到点标记集合范围
+	 * @param {Array} markerArray 点标记集合,必填
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	zoomToMarkerArray(markerArray, duration) {
+		duration = (duration != undefined) ? duration : 0
+		let coordinateArray = new Array()
+		for (let i = 0; i < markerArray.length; i++) {
+			coordinateArray.push(markerArray[i].Marker.getGeometry().getCoordinates())
+		}
+		let extentBound = new Extent.boundingExtent(coordinateArray)
+		this.view.fit(extentBound, {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 缩放到文本标记图层范围
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	zoomToLabelLayer(duration) {
+		const vm = this;
+		duration = (duration != undefined) ? duration : 0
+		this.view.fit(vm.labelLayer.getSource().getExtent(), {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 缩放到文本标记集合范围
+	 * @param {Array}  labelArray 文本标记集合,必填
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	zoomToLabelArray(labelArray, duration) {
+		duration = (duration != undefined) ? duration : 0
+		let coordinateArray = new Array()
+		for (let i = 0; i < labelArray.length; i++) {
+			coordinateArray.push(labelArray[i].Label.getGeometry().getCoordinates())
+		}
+		let extentBound = new Extent.boundingExtent(coordinateArray)
+		this.view.fit(extentBound, {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 缩放到线图层范围
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	zoomToPolylineLayer(duration) {
+		const vm = this
+		duration = (duration != undefined) ? duration : 0
+		this.view.fit(vm.polyLineLayer.getSource().getExtent(), {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 缩放到线标记集合范围
+	 * @param {Array} lineArray 线标记集合,必填
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	zoomToPolylineArray(lineArray, duration) {
+		duration = (duration != undefined) ? duration : 0
+		let coordinateArray = new Array()
+		for (let i = 0; i < lineArray.length; i++) {
+			let coordinates = lineArray[i].polyline.getGeometry().getCoordinates()
+			for (let z = 0; z < coordinates.length; z++) {
+				coordinateArray.push(coordinates[z])
+			}
+		}
+		let extentBound = new Extent.boundingExtent(coordinateArray)
+		this.view.fit(extentBound, {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 缩放到经纬度数组范围
+	 * @param {Array} lngLatArray KMap.LngLat格式的经纬度坐标数组,必填
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	zoomToLngLatArray(lngLatArray, duration) {
+		duration = (duration != undefined) ? duration : 0
+		let coordinateArray = new Array()
+		for (let i = 0; i < lngLatArray.length; i++) {
+			let point = Common.KMapLngLat2MapLngLat(lngLatArray[i])
+			coordinateArray.push(proj.fromLonLat(point))
+		}
+		let extentBound = new Extent.boundingExtent(coordinateArray)
+		this.view.fit(extentBound, {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 调整地图视角到能够显示所有覆盖物的合适矩形范围
+	 * @param {number} duration 选填参数,动画时长(单位:毫秒),不填则使用默认的0毫秒
+	 * @memberof Map
+	 */
+	setFitView(duration) {
+		const vm = this
+		//获取所有元素坐标点集合
+		let LonLatArray = new Array()
+		let markers = vm.markerLayer.getSource().getFeatures()
+		let labels = vm.labelLayer.getSource().getFeatures()
+		let polylines = vm.polyLineLayer.getSource().getFeatures()
+		let features = [markers, labels, polylines]
+		for (let i = 0; i < features.length; i++) {
+			for (let z = 0; z < features[i].length; z++) {
+				let featureLonLats = features[i][z].getGeometry().getCoordinates()
+				if (features[i] != polylines) {
+					LonLatArray.push(featureLonLats)
+				}
+				else {
+					for (let m = 0; m < featureLonLats.length; m++) {
+						LonLatArray.push(featureLonLats[m])
+					}
+				}
+			}
+		}
+		//地图视角切换到坐标点集合的矩形范围
+		duration = (duration != undefined) ? duration : 0
+		let extentBound = new Extent.boundingExtent(LonLatArray)
+		this.view.fit(extentBound, {
+			duration: duration
+		})
+	}
+
+	/**
+	 * @description 获取地图分辨率
+	 * @returns {number} 地图分辨率
+	 * @memberof Map
+	 */
+	getResolution() {
+		let resolution = this.view.getResolution()
+		return resolution
+	}
+
+	/**
+	 * @description 获取地图当前缩放值
+	 * @returns {number} 地图缩放级别
+	 * @memberof Map
+	 */
+	getZoom() {
+		let zoom = this.view.getZoom()
+		return zoom
+	}
+
+	/**
+	 * @description 设置地图当前缩放值
+	 * @param {number}zoom 缩放值,必填
+	 * @memberof Map
+	 */
+	setZoom(zoom) {
+		this.view.setZoom(zoom)
+	}
+
+	/**
+	 * @description 获取地图最大缩放值
+	 * @returns {number} 最大缩放值
+	 * @memberof Map
+	 */
+	getMaxZoom() {
+		let maxZoom = this.view.getMaxZoom()
+		return maxZoom
+	}
+
+	/**
+	 * @description 设置地图最大缩放值
+	 * @param {number} zoom 最大缩放值,必填
+	 * @memberof Map
+	 */
+	setMaxZoom(zoom) {
+		this.view.setMaxZoom(zoom)
+	}
+
+	/**
+	 * @description 获取地图最小缩放值
+	 * @returns {number} 最小缩放值
+	 * @memberof Map
+	 */
+	getMinZoom() {
+		let minZoom = this.view.getMinZoom()
+		return minZoom
+	}
+
+	/**
+	 * @description 设置地图最小缩放值
+	 * @param {number} zoom 最小缩放值,必填
+	 * @memberof Map
+	 */
+	setMinZoom(zoom) {
+		this.view.setMinZoom(zoom)
+	}
+
+	/**
+	 * @description 设置地图中心和缩放级别
+	 * @param {number} zoom 缩放级别,必填
+	 * @param {KMap.LngLat} center 地图中心 KMap.LngLat对象格式,必填
+	 * @param {boolean} animate 选填,是否使用缓冲动画,默认为false
+	 * @memberof Map
+	 */
+	setZoomAndCenter(zoom, center, animate) {
+		let centerlnglat = Common.KMapLngLat2MapLngLat(center)
+		center = proj.fromLonLat(centerlnglat)
+		if (animate) {
+			this.view.animate({ center: center, zoom: zoom })
+		}
+		else {
+			this.view.setCenter(center)
+			this.view.setZoom(zoom)
+		}
+	}
+
+	/**
+	 * @description 获取地图经纬度矩形范围
+	 * @returns {KMap.Bounds} 地图经纬度矩形范围,KMap.Bounds格式
+	 * @memberof Map
+	 */
+	getBounds() {
+		const vm = this;
+		let bounds = vm.view.calculateExtent(vm.map.getSize())
+		let southWest = proj.toLonLat([bounds[0], bounds[1]])
+		let northEast = proj.toLonLat([bounds[2], bounds[3]])
+		bounds = [southWest[0], southWest[1], northEast[0], northEast[1]]
+		let mapBound = Common.MapBounds2KMapBounds(bounds)//将OL的Bounds格式转换成KMap的Bounds格式
+		return mapBound
+	}
+
+	/**
+	 * @description 设置地图经纬度矩形范围
+	 * @param {KMap.Bounds} bound 地图经纬度矩形范围,KMap.Bounds格式,必填
+	 * @memberof Map
+	 */
+	setBounds(bound) {
+		let lnglatArray = new Array()
+		let mapBound = Common.KMapBounds2MapBounds(bound)//将KMap的Bounds格式转换成OL的Bounds格式
+		lnglatArray.push(proj.fromLonLat([mapBound[0], mapBound[1]]))
+		lnglatArray.push(proj.fromLonLat([mapBound[2], mapBound[3]]))
+		let bounds = new Extent.boundingExtent(lnglatArray)
+		this.view.fit(bounds) //地图视角切换到矩阵范围
+	}
+	/**
+	 * @description 设置地图经纬度矩形范围
+	 * @param {[minLng,minLat,maxLng,maxLat]} bound 地图经纬度矩形范围,KMap.Bounds格式,必填
+	 * @memberof Map
+	 */
+	fitBounds(bound) {
+		let lnglatArray = new Array();
+		lnglatArray.push(proj.fromLonLat([bound[0], bound[1]]))
+		lnglatArray.push(proj.fromLonLat([bound[2], bound[3]]))
+		let bounds = new Extent.boundingExtent(lnglatArray)
+		this.view.fit(bounds) //地图视角切换到矩阵范围
+	}
+	fit(geometryOrExtent, padding) {
+		this.view.fit(geometryOrExtent, { duration: 500, padding })
+	}
+	/**
+	 * @description 平面地图像素坐标转经纬度坐标
+	 * @param {KMap.Pixel} pixel 平面地图像素坐标,格式为KMap.Pixel对象,必填
+	 * @returns {KMap.LngLat} 经纬度坐标,格式为KMap.LngLat对象
+	 * @memberof Map
+	 */
+	pixelToLngLat(pixel) {
+		pixel = Common.KMapPixel2MapPixel(pixel)
+		let lnglat = new proj.toLonLat(pixel)
+		return Common.MapLngLat2KMapLngLat(lnglat)
+	}
+
+	/**
+	 * @description 经纬度坐标转平面地图像素坐标
+	 * @param {KMap.LngLat} lnglat 经纬度坐标,格式为KMap.LngLat对象,必填
+	 * @returns {KMap.Pixel} 地图像素坐标,格式为KMap.Pixel对象
+	 * @memberof Map
+	 */
+	lnglatToPixel(lnglat) {
+		lnglat = Common.KMapLngLat2MapLngLat(lnglat)
+		let pixel = proj.fromLonLat(lnglat)
+		return Common.MapPixel2KMapPixel(pixel)
+	}
+
+	/**
+	 * @description 地图容器屏幕坐标转经纬度坐标
+	 * @param {KMap.Pixel} pixel 地图容器像素,格式为KMap.Pixel对象,必填
+	 * @returns {KMap.LngLat} 返回KMap.LngLat格式的经纬度
+	 * @memberof Map
+	 */
+	containerToLngLat(pixel) {
+		pixel = Common.KMapPixel2MapPixel(pixel)
+		let lnglat = this.map.getCoordinateFromPixel(pixel)
+		lnglat = proj.toLonLat(lnglat)
+		lnglat = Common.MapLngLat2KMapLngLat(lnglat)
+		return lnglat
+	}
+
+	/**
+	 * @description 经纬度坐标转地图容器屏幕坐标
+	 * @param {KMap.LngLat} lnglat 经纬度坐标,KMap.LngLat格式的经纬度,必填
+	 * @returns {KMap.Pixel} 返回地图容器像素,格式为KMap.Pixel对象
+	 * @memberof Map
+	 */
+	lngLatToContainer(lnglat) {
+		lnglat = Common.KMapLngLat2MapLngLat(lnglat)
+		let coordinate = proj.fromLonLat(lnglat)
+		let container = this.map.getPixelFromCoordinate(coordinate)
+		return Common.MapPixel2KMapPixel(container)
+	}
+
+	/**
+	 * @description 获取地图顺时针旋转角度
+	 * @returns {number} 顺时针旋转角度(弧度)
+	 * @memberof Map
+	 */
+	getRotation() {
+		let rotation = this.view.getRotation()
+		return rotation
+	}
+
+	/**
+	 * @description 设置地图顺时针旋转角度
+	 * @param {number} rotation 顺时针旋转角度(弧度),必填
+	 * @memberof Map
+	 */
+	setRotation(rotation) {
+		this.view.setRotation(rotation)
+	}
+
+	/**
+	 * @description 获取地图插件集合
+	 * @returns {Array} 地图插件集合数组
+	 * @memberof Map
+	 */
+	getControls() {
+		let controls = this.map.getControls().array_
+		return controls
+	}
+
+	/**
+	 * @description 添加插件
+	 * @param {ol.control} control OL原生control对象
+	 * @memberof Map
+	 */
+	addControl(control) {
+		let state = true
+		let controls = this.map.getControls().array_
+		for (let i = 0; i < controls.length; i++) {
+			if (control == controls[i]) {
+				state = false
+				break
+			}
+		}
+		if (state) {
+			this.map.addControl(control)
+		}
+	}
+
+	/**
+	 * @description 删除插件
+	 * @param {ol.control} control 插件,必填
+	 * @memberof Map
+	 */
+	removeControl(control) {
+		let controls = this.map.getControls().array_
+		for (let i = 0; i < controls.length; i++) {
+			if (control == controls[i]) {
+				this.map.removeControl(controls[i])
+				return
+			}
+		}
+	}
+
+	/**
+	 * @description 清空默认插件 注意如果要清除默认插件需要在加载其他插件前调用此函数
+	 * @memberof Map
+	 */
+	removeOriginControls() {
+		let controls = this.map.getControls().array_
+		for (let i = 0; i < controls.length; i++) {
+			this.map.removeControl(controls[i])
+		}
+	}
+
+	/**
+	 * @description 获取地图指针样式
+	 * @memberof Map
+	 */
+	getDefaultCursor() {
+		let mapContainer = this.map.getTargetElement()
+		let cursor = mapContainer.style.cursor
+		return cursor
+	}
+
+	/**
+	 * @description 设置地图指针样式
+	 * @param {String} cursorStyle 鼠标样式("default"默认指针,"pointer"小手,"move"移动指针, "text"文本指针,"wait"等待状态,"help"帮助),必填
+	 * @memberof Map
+	*/
+	setDefaultCursor(cursorStyle) {
+		let mapContainer = this.map.getTargetElement()
+		if (cursorStyle != undefined) {
+			mapContainer.style.cursor = cursorStyle
+		}
+		else {
+			mapContainer.style.cursor = "default"
+		}
+	}
+
+	/**
+	 * @description 设置鼠标悬停在元素上时的样式
+	 * @param {String} cursorStyle 鼠标样式("default"默认指针,"pointer"小手,"move"移动指针, "text"文本指针,"wait"等待状态,"help"帮助),必填
+	 * @memberof Map
+	*/
+	setFeatureCursor(cursorStyle) {
+		cursorStyle = (cursorStyle == undefined) ? "default" : cursorStyle
+		let mapContainer = this.map.getTargetElement()
+		let defaultCursor = mapContainer.style.cursor
+		this.map.on("pointermove", function (e) {
+			let features = this.map.forEachFeatureAtPixel(e.pixel, function (feature) { return feature })
+			if (features) {
+				mapContainer.style.cursor = cursorStyle
+			}
+			else {
+				mapContainer.style.cursor = defaultCursor
+			}
+		})
+	}
+
+	/**
+	 * @description 获取地图显示元素种类
+	 * @returns {Array} 地图显示元素种类集合
+	 * @memberof Map
+	 */
+	getFeatures() {
+		const vm = this
+		let features = new Array()
+		if (vm.baseLayer.getVisible() == true) {
+			features.push("Tile")
+		}
+		if (vm.markerLayer.getVisible() == true) {
+			features.push("Marker")
+		}
+		if (vm.labelLayer.getVisible() == true) {
+			features.push("Label")
+		}
+		if (vm.polyLineLayer.getVisible() == true) {
+			features.push("PolyLine")
+		}
+		return features
+	}
+
+	/**
+	 * @description 设置地图显示元素种类
+	 * @param {JSON} param param 地图元素显示参数,JSON对象,必填
+	 * param.marker true/false,点标记是否显示,选填
+	 * param.label true/false,文本标记是否显示,选填
+	 * param.polyline true/false,线标记是否显示,选填
+	 * @memberof Map
+	 */
+	setFeatures(param) {
+		const vm = this
+		if (param.marker == true || param.marker == false) {
+			vm.markerLayer.setVisible(param.marker)
+		}
+
+		if (param.label == true || param.label == false) {
+			vm.labelLayer.setVisible(param.label)
+		}
+
+		if (param.polyline == true || param.polyline == false) {
+			vm.polyLineLayer.setVisible(param.polyline)
+		}
+
+	}
+
+	/**
+	 * @description 获取地图状态(双击缩放/拖拽/滚动鼠标中间缩放)
+	 * @returns {JSON} 地图状态
+	 * {"DoubleClickZoom": true, "DragAndDrop": true, "MouseWheelZoom": true}
+	 * @memberof Map
+	 */
+	getStates() {
+		let interactions = this.map.getInteractions().array_
+		let DoubleClickZoom, DragAndDrop, MouseWheelZoom
+		for (let i = 0; i < interactions.length; i++) {
+			if (i == 1) DoubleClickZoom = interactions[i].getActive()
+			if (i == 2) DragAndDrop = interactions[i].getActive()
+			if (i == 7) MouseWheelZoom = interactions[i].getActive()
+		}
+		let states = {
+			"DoubleClickZoom": DoubleClickZoom,
+			"DragAndDrop": DragAndDrop,
+			"MouseWheelZoom": MouseWheelZoom
+		}
+		return states
+	}
+
+	/**
+	 * @description 设置地图状态(双击缩放/拖拽/滚动鼠标中间缩放)
+	 * @param {JSON} param 地图状态 JSON对象,必填
+	 * param.DoubleClickZoom true/false(双击缩放地图),选填
+	 * param.DragAndDrop true/false(地图拖拽),选填
+	 * param.MouseWheelZoom true/false(滚动鼠标中间缩放地图),选填
+	 * @memberof Map
+	 */
+	setStates(param) {
+		let interactions = this.map.getInteractions().array_
+		if (param.DoubleClickZoom == true || param.DoubleClickZoom == false)
+			interactions[1].setActive(param.DoubleClickZoom)
+		if (param.DragAndDrop == true || param.DragAndDrop == false)
+			interactions[2].setActive(param.DragAndDrop)
+		if (param.MouseWheelZoom == true || param.MouseWheelZoom == false)
+			interactions[7].setActive(param.MouseWheelZoom)
+	}
+
+	/**
+	 * @description 清空地图所有元素
+	 * @memberof Map
+	 */
+	clearMap() {
+		const vm = this
+		//清空图层元素
+		vm.markerLayer.getSource().clear()
+		vm.labelLayer.getSource().clear()
+		vm.polyLineLayer.getSource().clear()
+		vm.polygonLayer.getSource().clear()
+		//清空地图弹窗
+		vm.clearInfoWindow()
+	}
+
+	/**
+	 * @description 清除地图对象并清空地图容器(建议少使用此API,容易造成报错)
+	 * @memberof Map
+	 */
+	destroy() {
+		//清空地图对象
+		// this.map.destroy()
+		//清空地图容器
+		let target = this.map.getTargetElement()
+		target.innerHTML = ""
+		Common.UseBaiDuOnlineLayer = false;
+		Common.UseGaoDeOnlineLayer = false;
+		Common.UseWGS84OnlineLayer = false;
+	}
+
+	/**
+	 * @description 获取地图图层数组
+	 * @return {Array} 地图图层数组
+	 * @memberof Map
+	 */
+	getLayers() {
+		let layers = this.map.getLayers().array_
+		return layers
+	}
+
+	/**
+	 * @description 设置地图各个图层显示/隐藏
+	 * @param {JSON} param 地图图层显示参数,JSON对象,必填
+	 * param.Marker true/false,点标记图层显示/隐藏,选填
+	 * param.Label true/false,文本标记图层显示/隐藏,选填
+	 * param.PolyLine true/false,线标记图层显示/隐藏,选填
+	 * @memberof Map
+	 */
+	setLayers(param) {
+		const vm = this
+		if (param.Marker == true || param.Marker == false) {
+			vm.markerLayer.setVisible(param.Marker)
+		}
+		if (param.Label == true || param.Label == false) {
+			vm.labelLayer.setVisible(param.Label)
+		}
+		if (param.PolyLine == true || param.PolyLine == false) {
+			vm.polyLineLayer.setVisible(param.PolyLine)
+		}
+	}
+
+	/**
+	 * @description 获取地图各个图层index值
+	 * @returns {JSON} JSON对象
+	 * {"markerLayer": 0, "labelLayer": 0, "polyLineLayer": 0}
+	 * @memberof Map
+	 */
+	getLayersIndex() {
+		const vm = this
+		let index = {}
+		index.markerLayer = vm.markerLayer.getZIndex()
+		index.labelLayer = vm.labelLayer.getZIndex()
+		index.polyLineLayer = vm.polyLineLayer.getZIndex()
+		return index
+	}
+
+	/**
+	 * @description 设置地图各个图层index值,index值大的图层显示在上方,值相同时后加载的图层显示在上方
+	 * @param {JSON} param 图层索引JSON对象,必填
+	 * param.marker 点标记图层index值,选填
+	 * param.label 文字标记图层index值,选填
+	 * param.polyLine 线标记图层index值,选填
+	 * @memberof Map
+	 */
+	setLayersIndex(param) {
+		const vm = this
+		let markerIndex = (param.marker) ? param.marker : vm.markerLayer.getZIndex()
+		let labelIndex = (param.label) ? param.label : vm.labelLayer.getZIndex()
+		let polyLineIndex = (param.polyLine) ? param.polyLine : vm.polyLineLayer.getZIndex()
+		vm.markerLayer.setZIndex(markerIndex)
+		vm.labelLayer.setZIndex(labelIndex)
+		vm.polyLineLayer.setZIndex(polyLineIndex)
+	}
+
+	/**
+	 * @description 注册地图事件
+	 * @param {String} eventName 地图操作事件类型,必填
+	 * "click":单击地图,双击将触发两次;"singleclick":单击地图;"dblclick":双击地图;"movestart":开始移动地图;
+	 * "moveend":移动地图结束;"postrender":渲染地图后;"pointerdrag":拖动指针时;"mousemove":移动指针时
+	 * @param {funciton} callback 操作事件触发时调用的函数,必填
+	 * @memberof Map
+	 */
+	on(eventName, callback) {
+		if (eventName == "mousemove") {
+			eventName = "pointermove"
+		}
+		if (eventName == "load") {
+			eventName = "postrender"
+		}
+		this.map.on(eventName, callback)
+	}
+
+	/**
+	 * @description 注册地图事件(事件仅执行一次)
+	 * @param {Strig} eventName 地图操作事件类型,必填
+	 * "click":单击地图,双击将触发两次;"singleclick":单击地图;"dblclick":双击地图;"movestart":开始移动地图;
+	 * "moveend":移动地图结束;"postrender":渲染地图后;"pointerdrag":拖动指针时;"mousemove":移动指针时
+	 * @param {function} callback 操作事件触发时调用的函数,必填
+	 * @memberof Map
+	 */
+	once(eventName, callback) {
+		if (eventName == "mousemove") {
+			eventName = "pointermove"
+		}
+		if (eventName == "load") {
+			eventName = "postrender"
+		}
+		this.map.once(eventName, callback)
+	}
+
+	/**
+	 * @description 取消地图绑定事件
+	 * @param {Strig} eventName 地图操作事件类型,必填
+	 * "click":单击地图,双击将触发两次;"singleclick":单击地图;"dblclick":双击地图;"movestart":开始移动地图;
+	 * "moveend":移动地图结束;"postrender":渲染地图后;"pointerdrag":拖动指针时;"mousemove":移动指针时
+	 * @param {function} callback 操作事件触发时调用的函数,必填
+	 * @memberof Map
+	 */
+	off(eventName, callback) {
+		if (eventName == "mousemove") {
+			eventName = "pointermove"
+		}
+		if (eventName == "load") {
+			eventName = "postrender"
+		}
+		this.map.un(eventName, callback)
+	}
+	setMarkerLayerZoomInfo(minZoom, maxZoom) {
+		this.markerLayer.setMaxZoom(maxZoom);
+		this.markerLayer.setMinZoom(minZoom);
+	}
+	setPolylineLayerZoomInfo(minZoom, maxZoom) {
+		this.polylineLayer.setMaxZoom(maxZoom);
+		this.polylineLayer.setMinZoom(minZoom);
+	}
+	setPolygonLayerZoomInfo(minZoom, maxZoom) {
+		this.polygonLayer.setMaxZoom(maxZoom);
+		this.polygonLayer.setMinZoom(minZoom);
+	}
+}
+export default Map

+ 566 - 0
src/utils/ol-map/Marker.js

@@ -0,0 +1,566 @@
+import Feature from 'ol/Feature'
+import Point from 'ol/geom/Point'
+import * as proj from 'ol/proj'
+import Style from 'ol/style/Style'
+import Text from 'ol/style/Text'
+import Fill from 'ol/style/Fill'
+import Stroke from 'ol/style/Stroke'
+import Icon from 'ol/style/Icon'
+import Overlay from 'ol/Overlay'
+import Common from './Common'
+import KBaseObject from './KBaseObject'
+import LngLat from './LngLat'
+import InfoWindow from './InfoWindow'
+/**
+ * @description KMap.Marker 点标记类
+ */
+class Marker extends KBaseObject{
+	/**
+	 * @description 初始化Marker构造函数
+	 * @param {number} lng lng 经度 必填
+	 * @param {number} lat lat 纬度 必填
+	 * @param {String} markerImgUrl 点标记图标路径 必填
+	 * @param {number} offsetX X方向偏移量(正向右,负向左) 必填
+	 * @param {number} offsetY Y方向偏移量(正向下,负向上) 必填
+	 * @param {number} width  点标记尺寸宽度 必填
+	 * @param {number} height 点标记尺寸高度 必填
+	 * @param {JSON} param param.source 指定source 选填
+	 * @param {KMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @constructor
+	 */
+	constructor(lng,lat,url,offsetX,offsetY,width,height,param,mapInstance = null){
+		super(mapInstance)
+		const vm = this
+		//对于必填的参数进行验证
+		let lnglat = [lng,lat];
+		if(vm.map.getView().getProjection() == proj.get("EPSG:3857")){
+			lnglat = proj.fromLonLat([lng,lat]);
+		}
+		vm.point = new Point(lnglat)
+		let marker = new Feature({
+			geometry: vm.point,
+			properties: null
+		})
+		vm.marker = marker
+		vm.source = vm.mapInstance.markerLayer.getSource()
+		if(param && param.source){
+			vm.source = param.source
+		}
+		vm.source.addFeature(marker)
+		Common.notEmpty("url",url)
+		vm.url = url
+		vm.style = vm.initSyle(vm.url,offsetX,offsetY,width,height)
+		vm.image = vm.style.getImage()
+		vm.contentInfo = {}
+		Common.checkLngLat(lng,lat)
+		vm.lngLat = [lng,lat]
+	}
+
+	/**
+	 * @description 初始化Marker样式
+	 * @param {String} url marker 图片地址
+	 * @param {number} offsetX  X方向偏移量(正向右,负向左) 必填
+	 * @param {number} offsetY  Y方向偏移量(正向下,负向上) 必填
+	 * @param {number} width  点标记尺寸宽度 必填
+	 * @param {number} height 点标记尺寸高度 必填
+	 * @memberof Marker
+	 */
+	initSyle(url,offsetX,offsetY,width,height){
+		const vm = this
+		offsetX = -offsetX/width
+		offsetY = -offsetY/height
+		let style = new Style({
+			image: new Icon({
+				src: url,
+				anchor: [offsetX,offsetY] //点标记偏移
+			}),
+			zIndex: 0
+		})
+		vm.marker.setStyle(style)
+		return style
+	}
+
+	/**
+	 * @description 获取点标记id
+	 * @returns {String} 点标记id
+	 */
+	getId() {
+		const vm = this
+		let id = vm.marker.getId()
+		return id
+	}
+
+	/**
+	 * @description 设置点标记id
+	 * @param {String}id 点标记id 必填
+	 */
+	setId(id) {
+		const vm = this
+		vm.marker.setId(id)
+	}
+
+	/**
+	 * 点标记添加文本
+	 * @param {JSON} param json对象,文本参数,必填
+	 * param.text 文本内容,必填
+	 * param.font 字体大小,默认为13px sans-serif,选填
+	 * param.offsetX X轴偏移(正向右,负向左),选填,默认为0
+	 * param.offsetY Y轴偏移(正向下,负向上),选填,默认为25
+	 * param.rotation 旋转角度,选填,默认0
+	 * param.fill 填充颜色,选填,默认black
+	 * param.backgroundColor 背景色,选填,默认#fff
+	 * param.backgroundStroke 边框颜色,选填,默认black
+	 * param.padding padding值,选填,默认[2,5,2,5]
+	 */
+	setText(param) {
+		const vm = this
+		let text = (param.text)? param.text : ""
+		let font = (param.font)? param.font : '13px sans-serif'
+		let offsetX = (param.offsetX)? param.offsetX : 0
+		let offsetY = (param.offsetY)? param.offsetY : 25
+		let rotation = (param.rotation)? param.rotation : 0
+		let fill = (param.fill)? param.fill : "black"
+		let backgroundColor = (param.backgroundColor)? param.backgroundColor : "#fff"
+		let backgroundStroke = (param.backgroundStroke)? param.backgroundStroke : "black"
+		let padding = (param.padding)? param.padding : [2,5,2,5]
+		text = new Text({
+			text: text,
+			font: font,
+			offsetX: offsetX,
+			offsetY: offsetY,
+			rotateWithView: false,//文本是否可旋转
+			rotation: rotation,
+			fill: new Fill({
+				color: fill
+			}),
+			backgroundFill: new Fill({
+				color: backgroundColor
+			}),
+			backgroundStroke: new Stroke({
+				color: backgroundStroke
+			}),
+			padding: padding
+		})
+		if(text){
+			vm.style.setText(text)
+		}
+	}
+
+	/**
+	 * @description 显示点标记
+	 */
+	show(){
+		const vm = this
+		if(!vm.source.hasFeature(vm.marker)) {
+			vm.source.addFeature(vm.marker)
+			if(vm.markerPopup){
+				vm.map.removeOverlay(vm.markerPopup)
+			}
+			vm.showContent()
+		}
+	}
+
+	/**
+	 * @description 隐藏点标记
+	 */
+	hide(){
+		const vm = this
+		if(vm.source.hasFeature(vm.marker)) {
+			vm.source.removeFeature(vm.marker);
+			if(vm.markerPopup){
+				vm.map.removeOverlay(vm.markerPopup)
+			}
+			vm.hideContent()
+		}
+	}
+
+	/**
+	 * @description 删除点标记
+	 */
+	remove(){
+		const vm = this
+		if(vm.source.hasFeature(vm.marker)) {
+			vm.source.removeFeature(vm.marker)
+			if(vm.markerPopup){
+				vm.map.removeOverlay(vm.markerPopup)
+			}
+			vm.hideContent()
+		}
+	}
+
+	/**
+	 * @description 设置点标记为透明样式
+	 */
+	setHideStyle(){
+		const vm = this
+		let hideStyle = new Style({
+			stroke: new Stroke({
+				color: "transparent",
+				width: 1
+			})
+		})
+		vm.marker.setStyle(hideStyle)
+	}
+
+	/**
+	 * @description 获取点标记图标地址
+	 * @returns {String} 点标记图标地址
+	 */
+	getIcon(){
+		const vm = this
+		let icon = vm.image.iconImage_.src_
+		return icon
+	}
+
+	/**
+	 * @description 设置点标记图标
+	 * @param {String} url 点标记图标地址,必填
+	 */
+	setIcon(url,offsetX,offsetY){
+		const vm = this
+		let style = new Style({
+			image: new Icon({
+				src: url,
+				anchor: [offsetX,offsetY] //点标记偏移
+			}),
+			zIndex: 0
+		})
+		vm.marker.setStyle(style)
+	}
+
+	/**
+	 * @description 获取点标记坐标
+	 * @returns {KMap.LngLat} 返回KMap.LngLat格式的经纬度
+	 */
+	getPosition() {
+		const vm = this
+		let position = proj.toLonLat(vm.point.getCoordinates())
+		position = Common.MapLngLat2KMapLngLat(position)
+		return position
+	}
+
+	/**
+	 * @description 设置点标记坐标
+	 * @param {KMap.LngLat} lnglat 格式的点标记经纬度,必填
+	 */
+	setPosition(lnglat) {
+		const vm = this
+		lnglat = Common.KMapLngLat2MapLngLat(lnglat)
+		let position = proj.fromLonLat(lnglat)
+		vm.point.setCoordinates(position)
+		// console.log(vm.source.getState())
+		if(vm.markerPopup){
+			vm.markerPopup.setPosition(vm.point.getCoordinates())
+		}
+	}
+
+	/**
+	 * @description 获取点标记缩放值
+	 * @returns 点标记缩放值
+	 */
+	getScale() {
+		const vm = this
+		let scale = vm.image.getScale()
+		return scale
+	}
+
+	/**
+	 * @description 设置点标记缩放值
+	 * @param {number}scale 缩放值,必填
+	 */
+	setScale(scale) {
+		const vm = this
+		vm.image.setScale(scale)
+	}
+
+
+	/**
+	 * @description 获取点标记旋转弧度
+	 * @returns 点标记旋转弧度
+	 */
+	getAngle() {
+		const vm = this
+		let angle = vm.image.getRotation()
+		return angle
+	}
+
+	/**
+	 * @description 设置点标记旋转弧度
+	 * @param {number} rotation 旋转弧度,必填
+	 */
+	setAngle(rotation) {
+		const vm = this
+		vm.image.setRotation(rotation)
+	}
+
+	/**
+	 * @description 获取点标记叠加顺序
+	 * @returns 点标记叠加顺序
+	 */
+	getZIndex() {
+		const vm = this
+		let index = vm.style.getZIndex()
+		return index
+	}
+
+	/**
+	 * @description 设置点标记叠加顺序
+	 * @param {number} index 叠加顺序,必填,index值较大的点标记显示在上方 ,值相同时后创建的点标记显示在上方
+	 */
+	setZIndex(index) {
+		const vm = this
+		vm.style.setZIndex(index)
+	}
+
+	/**
+	 * @description 设置点标记置顶,当地图有多个marker时,当isTop为true时marker将显示在最上层;当isTop为false时取消置顶
+	 * @param {boolean} isTop 必填,true:显示在最上层,false:取消置顶
+	 */
+	setTop(isTop) {
+		const vm = this
+		let markers = vm.source.getFeatures()
+		let maxIndex = 0
+		for(let i=0; i<markers.length; i++) {
+			if(markers[i].getStyle().getZIndex() > maxIndex)
+				maxIndex = markers[i].getStyle().getZIndex()
+		}
+		if(isTop == true) {
+			vm.style.setZIndex(++maxIndex)
+		}
+		else if(isTop == false) {
+			vm.style.setZIndex(0)
+		}
+	}
+
+	/**
+	 * @description 获取用户自定义属性
+	 * @returns {number,String,JSON} 用户自定义属性(支持数字,字符串,json)
+	 */
+	getExtData() {
+		const vm = this
+		let extData = vm.marker.values_.properties
+		return extData
+	}
+
+	/**
+	 * @description 设置用户自定义属性
+	 * @param {number/String/JSON} extData 用户自定义属性(支持数字,字符串,json),必填
+	 */
+	setExtData(extData) {
+		const vm = this
+		extData = (extData)? extData : null
+		if(extData != null) {
+			vm.marker.values_.properties = extData
+		}
+	}
+
+	/**
+	 * @description 获取点标记地图对象
+	 * @returns 地图对象
+	 */
+	getMap() {
+		const vm = this
+		return vm.mapInstance
+	}
+
+	/**
+	 * @description 设置点标记地图对象,传入null时,移除点标记,
+	 * 建议不使用此API,使用remove方法
+	 * @param {KMap.Map} map 传入null,移除点标记
+	 */
+	setMap(mapInstance){
+		const vm = this
+		mapInstance = (mapInstance)? mapInstance : null
+		if(mapInstance == null) { //当map为null时,删除点标记
+			if(vm.source.hasFeature(vm.marker)) {
+				vm.source.removeFeature(vm.marker)
+				if(vm.markerPopup){
+					vm.map.removeOverlay(vm.markerPopup)
+				}
+				vm.hideContent()
+			}
+		}else{
+			if(mapInstance != vm.mapInstance){
+				vm.mapInstance = mapInstance
+
+			}
+		}
+	}
+
+	/**
+	 * @description 注册点标记事件
+	 * @param {String} eventName 鼠标事件
+	 * "click":鼠标点击事件 "mousemove":鼠标悬停事件
+	 * @param {function} callback 选中点标记时触发函数
+	 */
+	on(eventName,callback){
+		const vm = this
+		if(eventName == "dblclick"){
+			vm.map.on(eventName,function(e){
+				let feature = vm.map.forEachFeatureAtPixel(e.pixel,function(feature) {
+					return feature
+				},{
+					layerFilter:function(e){
+						if(e.values_.name == "defaultMarkerLayer"){
+							return true
+						}else{
+							return false
+						}
+					}
+				})
+				if(feature && feature == vm.marker){
+					callback(e)
+				}
+			})
+		}else{
+			vm.marker.on(eventName,function(e) {
+				if(eventName == "mousemove"){
+					if(vm.marker.ol_uid != vm.mapInstance.InfowindowmoveUID)
+					{
+						vm.mapInstance.InfowindowmoveUID = vm.marker.ol_uid
+						callback(e)
+					}
+				}
+				else
+				{
+					callback(e)
+				}
+			})
+		}
+	}
+
+	/**
+	 * @description 设置鼠标悬停时,点标记显示内容
+	 * @param {title} title 鼠标悬停内容(支持div模块/字符串格式),必填
+	 * @param {number} offsetX X轴方向偏移量(正向右,负向左),选填参数,默认为0
+	 * @param {number} offsetY Y轴方向偏移量(正向下,负向上),选填参数,默认为-25
+	 */
+	setTitle(title,offsetX,offsetY) {
+		const vm = this
+		title = (title != undefined)? title : ""
+		let lnglat = vm.getPosition()
+		vm.on("mousemove",function(){
+			let content
+			//if(title.indexOf("div")<0) { content = "<div class='olText'>"+title+"</div>" }
+			if(title.indexOf("div")<0) {
+				content = "<div class='olText'><p class='olTextP' title='"+title+"'>"+title+"</p></div>"
+			}else {
+				content = title
+			}
+			let infoWindow = new InfoWindow({
+				content: content,
+				position: lnglat,
+				offsetX: offsetX,
+				offsetY: offsetY,
+				type: "mousemove"
+			})
+			infoWindow.open(false) //显示标题内容
+		})
+	}
+
+	/**
+	 * @description 设置挂载Label
+	 * @param {DOM} content DOM元素
+	 * @param {number} offsetX DOM元素X偏移,向左为负,向右为正
+	 * @param {number} offsetY DOM元素X偏移,向上为负,向下为正
+	 * @memberof Marker
+	 */
+	setLabel(content,offsetX,offsetY){
+		const vm = this
+		let contentParent = document.createElement('div')
+		contentParent.insertAdjacentHTML('beforeend',content)
+		let offset = [0,0]
+		if(offsetX){
+			offset[0] = offsetX
+		}
+		if(offsetY){
+			offset[1] = offsetY
+		}
+		vm.markerPopup = new Overlay({
+			element: contentParent,
+			position: proj.fromLonLat([vm.lng,vm.lat]),
+			offset: offset//图片偏移量
+		})
+		vm.map.addOverlay(vm.markerPopup)
+	}
+
+	/**
+	 * @description 移除
+	 * @memberof Marker
+	 */
+	removeLabel(){
+		const vm = this
+		if(vm.markerPopup){
+			vm.map.removeOverlay(vm.markerPopup)
+		}
+	}
+
+	/**
+	 * @description 隐藏挂载dom元素
+	 */
+	hideContent(){
+		const vm = this
+		if(vm.contentInfo.overlay && vm.contentInfo.show){
+			vm.contentInfo.show = false
+			vm.map.removeOverlay(vm.contentInfo.overlay)
+		}
+	}
+
+	/**
+	 * @description 显示挂载dom元素
+	 */
+	showContent(){
+		const vm = this
+		if(vm.contentInfo.overlay && !vm.contentInfo.show){
+			vm.contentInfo.show = true
+			vm.map.addOverlay(vm.contentInfo.overlay)
+		}
+	}
+
+	/**
+	 * @description 设设置挂载的DOM,
+	 * @param {DOM} content 挂载到Map的DOM元素
+	 * @param {number} offsetX DOM元素在地图上X偏移,向左为负,向右为正
+	 * @param {number} offsetY DOM元素在地图上Y偏移,向上为负,向下为正
+	 * @memberof Marker
+	 */
+	setContent(content,offsetX,offsetY){
+		const vm = this
+		let overlay = new Overlay({
+			element: content,
+			offset:[offsetX,offsetY]
+		})
+		vm.contentInfo.content = content
+		vm.contentInfo.offsetX = offsetX
+		vm.contentInfo.offsetY = offsetY
+		vm.contentInfo.overlay = overlay
+		vm.contentInfo.show = true
+		vm.map.addOverlay(overlay)
+		let coordinate = vm.lngLat
+		overlay.setPosition(proj.fromLonLat(coordinate))
+	}
+
+	/**
+	 * @description 设置挂载的DOM可变长度的,
+	 * @param {DOM} content DOM 挂载到Map的DOM元素
+	 * @param {number} offsetY DOM元素在地图上Y偏移,向上为负,向下为正
+	 * @memberof Marker
+	 */
+	setContentChangeAbleWidth(content,offsetY){
+		const vm = this
+		let overlay = new Overlay({
+			element: content,
+			positioning:"bottom-center",
+			offset:[0,offsetY]
+		})
+		vm.contentInfo.content = content
+		vm.contentInfo.offsetX = 0
+		vm.contentInfo.offsetY = offsetY
+		vm.contentInfo.overlay = overlay
+		vm.contentInfo.show = true
+		vm.map.addOverlay(overlay)
+		let coordinate = vm.lngLat
+		overlay.setPosition(proj.fromLonLat(coordinate))
+	}
+}
+export default Marker

+ 108 - 0
src/utils/ol-map/MultiPolygon.js

@@ -0,0 +1,108 @@
+import OLPolygon from 'ol/geom/MultiPolygon'
+import Feature from 'ol/Feature'
+import Fill from 'ol/style/Fill'
+import Stroke from 'ol/style/Stroke'
+import Style from 'ol/style/Style'
+import * as proj from 'ol/proj'
+import * as olExtent from 'ol/extent'
+import KBaseObject from './KBaseObject'
+/**
+ * @description KMap.Polygon 面标记类
+*/
+class MultiPolygon extends KBaseObject{
+  /**
+   * @param {*} positions
+   * @param {*} style
+   * @param {KMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+   * @memberof Polygon
+   */
+  constructor(positions,style,options,mapInstance = null){
+    super(mapInstance)
+		const vm = this
+    const layer = vm.mapInstance.polygonLayer
+    vm.source = layer.getSource();
+    vm.style = vm.initStyle(style)
+    vm.polygon = vm.initFeature(positions,vm.style,options)
+    vm.source.addFeature(vm.polygon)
+  }
+  initStyle(style){
+    let fillColor = (style!=undefined && style.fillColor)?style.fillColor:'rgba(255,0,0,0.5)'
+    let strokeColor = (style!=undefined && style.strokeColor)?style.strokeColor:'rgba(255,0,0,1)'
+    let strokeWidth = (style!=undefined && style.strokeWidth)?style.strokeWidth:1
+    let fill = new Fill({
+      color: fillColor
+    })
+    let stroke = new Stroke({
+      color: strokeColor,
+      width: strokeWidth
+    })
+    let newStyle= new Style({
+      fill: fill,
+      stroke: stroke
+    })
+    return newStyle
+  }
+  initFeature(positions,style,options){
+    let geometry = new OLPolygon(positions);
+    if(options && options.projection){
+      let mapProjection  = this.map.getView().getProjection().getCode();
+      geometry.applyTransform(proj.getTransform(options.projection, mapProjection))
+    }
+    this.geometry = geometry;
+    //创建面标记
+    let polygon = new Feature({geometry:geometry})
+    polygon.setStyle(style)
+    return polygon
+  }
+
+  /**
+	 * 地图视角缩放到线标记范围
+	 * @param duration 动画持续时间(单位:毫秒) 选填,默认0毫秒
+	 */
+	zoomToExtent(duration) {
+    const vm = this
+		duration = (duration)? duration : 0
+		let extent = vm.polygon.getGeometry().getExtent()
+		// let extentBound = olExtent.boundingExtent(LonLatArray)
+		vm.map.getView().fit(extent,{
+			duration: duration
+		})
+	}
+
+  /**
+   * @description 显示
+   * @memberof Polygon
+   */
+  show(){
+    const vm = this
+		if(!vm.source.hasFeature(vm.polygon)) {
+			vm.source.addFeature(vm.polygon)
+		}
+  }
+
+  /**
+   * @description 隐藏
+   * @memberof Polygon
+   */
+  hide(){
+    const vm = this
+		if(vm.source.hasFeature(vm.polygon)) {
+			vm.source.removeFeature(vm.polygon)
+		}
+  }
+  /**
+   * @description 移除
+   * @memberof Polygon
+   */
+  remove(){
+    const vm = this
+		if(vm.source.hasFeature(vm.polygon)) {
+			vm.source.removeFeature(vm.polygon)
+		}
+  }
+  getGeometry(){
+    const vm = this;
+    return vm.geometry;
+  }
+}
+export default MultiPolygon

+ 55 - 0
src/utils/ol-map/Pixel.js

@@ -0,0 +1,55 @@
+/**
+ * @description KMap.Pixel 像素类
+*/
+class Pixel{
+  /**
+   * @description 像素类构造函数
+   * @param {number} x X像素,必填
+   * @param {number} y Y像素,必填
+   */
+  constructor(x,y){
+    let mapPixel = [Number(x),Number(y)];
+    this.pixel = mapPixel;
+  }
+
+  /**
+   * @description 获得X方向像素坐标
+   * @returns {number} 返回X方向像素坐标
+   */
+  getX() { 
+    return this.pixel[0]; 
+  }
+
+  /**
+   * @description 获得Y方向像素坐标
+   * @returns {number} 返回Y方向像素坐标
+   */
+  getY() { 
+    return this.pixel[1]; 
+  }
+
+  /**
+   * @description 当前像素坐标与传入像素坐标是否相等,必填
+   * @returns {boolean} 相等返回true,不相等返回false
+   */
+  equals(point) {
+      if(point.getX() == this.pixel[0] && point.getY() == this.pixel[1])
+      {
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+  }
+
+  /**
+   * @description 以字符串形式返回像素坐标对象
+   * @returns {String} 像素坐标字符串,用逗号连接
+   */
+  toString() { 
+    return this.pixel[0] + "," + this.pixel[1] ; 
+  }
+}
+
+export default Pixel

+ 108 - 0
src/utils/ol-map/Polygon.js

@@ -0,0 +1,108 @@
+import OLPolygon from 'ol/geom/Polygon'
+import Feature from 'ol/Feature'
+import Fill from 'ol/style/Fill'
+import Stroke from 'ol/style/Stroke'
+import Style from 'ol/style/Style'
+import * as proj from 'ol/proj'
+import * as olExtent from 'ol/extent'
+import KBaseObject from './KBaseObject'
+/**
+ * @description KMap.Polygon 面标记类
+*/
+class Polygon extends KBaseObject{
+  /**
+   * @param {*} positions
+   * @param {*} style
+   * @param {KMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+   * @memberof Polygon
+   */
+  constructor(positions,style,options,mapInstance = null){
+    super(mapInstance)
+		const vm = this
+    const layer = vm.mapInstance.polygonLayer
+    vm.source = layer.getSource();
+    vm.style = vm.initStyle(style)
+    vm.polygon = vm.initFeature(positions,vm.style)
+    vm.source.addFeature(vm.polygon)
+  }
+  initStyle(style){
+    let fillColor = (style!=undefined && style.fillColor)?style.fillColor:'rgba(255,0,0,0.5)'
+    let strokeColor = (style!=undefined && style.strokeColor)?style.strokeColor:'rgba(255,0,0,1)'
+    let strokeWidth = (style!=undefined && style.strokeWidth)?style.strokeWidth:1
+    let fill = new Fill({
+      color: fillColor
+    })
+    let stroke = new Stroke({
+      color: strokeColor,
+      width: strokeWidth
+    })
+    let newStyle= new Style({
+      fill: fill,
+      stroke: stroke
+    })
+    return newStyle
+  }
+  initFeature(positions,style){
+    let newPositions = []
+    positions.forEach(function(position){
+      if(position instanceof Array)
+        newPositions.push(position) 
+      else{
+        newPositions.push([position.getLng(),position.getLat()])
+      }
+    })
+    let geometry = new OLPolygon([newPositions])
+    geometry.applyTransform(proj.getTransform('EPSG:4326', 'EPSG:3857'))
+    //创建面标记
+    let polygon = new Feature({geometry:geometry})
+    polygon.setStyle(style)
+    return polygon
+  }
+
+  /**
+	 * 地图视角缩放到线标记范围
+	 * @param duration 动画持续时间(单位:毫秒) 选填,默认0毫秒
+	 */
+	zoomToExtent(duration) {
+    const vm = this
+		duration = (duration)? duration : 0
+		let extent = vm.polygon.getGeometry().getExtent()
+		// let extentBound = olExtent.boundingExtent(LonLatArray)
+		vm.map.getView().fit(extent,{
+			duration: duration
+		})
+	}
+
+  /**
+   * @description 显示
+   * @memberof Polygon
+   */
+  show(){
+    const vm = this
+		if(!vm.source.hasFeature(vm.polygon)) {
+			vm.source.addFeature(vm.polygon)
+		}
+  }
+
+  /**
+   * @description 隐藏
+   * @memberof Polygon
+   */
+  hide(){
+    const vm = this
+		if(vm.source.hasFeature(vm.polygon)) {
+			vm.source.removeFeature(vm.polygon)
+		}
+  }
+  /**
+   * @description 移除
+   * @memberof Polygon
+   */
+  remove(){
+    const vm = this
+		if(vm.source.hasFeature(vm.polygon)) {
+			vm.source.removeFeature(vm.polygon)
+		}
+  }
+}
+export default Polygon

+ 297 - 0
src/utils/ol-map/Polyline.js

@@ -0,0 +1,297 @@
+import Common from './Common'
+import Style from 'ol/style/Style'
+import Stroke from 'ol/style/Stroke'
+import * as olExtent from 'ol/extent'
+import * as proj from 'ol/proj'
+import InfoWindow from './InfoWindow'
+import LngLat from './LngLat'
+import Feature from 'ol/Feature'
+import LineString from 'ol/geom/LineString'
+import KBaseObject from './KBaseObject'
+/**
+ * @description KMap.Polyline 线标记类
+*/
+class Polyline extends KBaseObject{
+	/**
+	 * @param {*} points
+	 * @param {*} style
+	 * @param {*} extData
+	 * @param {KMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof Polyline
+	 */
+	constructor(points,style,extData,options,mapInstance = null) {
+    super(mapInstance)
+		//地图map对象
+    const vm = this
+    //地图线标记图层
+    vm.source = vm.mapInstance.polyLineLayer.getSource()
+		//线标记样式
+    extData = (extData)? extData : null
+
+    let point_Array = new Array()
+    for(let i=0; i<points.length; i++) {
+      let point = Common.KMapLngLat2MapLngLat(points[i])
+      point_Array.push(proj.fromLonLat(point))
+    }
+    
+    //创建线标记
+    let polyline = new Feature({
+      geometry: new LineString(point_Array),
+      properties: extData
+    })
+    vm.style = vm.initStyle(style)
+    polyline.setStyle(vm.style)
+
+    //线标记添加到地图图层
+    vm.source.addFeature(polyline)
+
+    //获取线标记
+    vm.polyline = polyline
+    vm.pointmoveFeature =null
+  }
+  initStyle(style){
+    //默认线路样式
+    let defStyle = {
+      color: "red",
+      width: 3,
+      lineCap: "round"
+    }
+    //需增加兼容老版本的代码
+    if(style != undefined && style.strokeColor != undefined){
+      defStyle.color = style.strokeColor
+    }
+    if(style != undefined && style.strokeWidth != undefined){
+      defStyle.width = style.strokeWidth
+    }
+    style = ( style != undefined ) ? Common.extend(defStyle,style,true) : defStyle
+    //设置线标记初始样式
+    let defaultStyle = new Style({
+      stroke: new Stroke(style),
+      zIndex: 0
+    })
+    return defaultStyle
+  }
+
+	/**
+	 * 获取id
+	 * @returns id
+	 */
+	getId() {
+    const vm = this
+		let id = vm.polyline.getId()
+		return id
+	}
+
+	/**
+	 * 设置id
+	 * @param id 线标记id值(尽量添加前缀不要纯数字),必填
+	 */
+	setId(id) {
+    const vm = this
+		vm.polyline.setId(id)
+	}
+
+	/**
+	 * 设置线标记样式
+	 * @param param JSON对象,线样式配置,选填,为空时使用默认配置
+	 * param.color 线颜色,选填,默认red
+	 * param.width 线宽度 选填,默认3
+	 * param.lineCap 线帽样式("butt"对接,"round"圆形,"square"方形) 选填,默认round
+	 */
+	setStyle(param) {
+    const vm = this
+		let color = (param != undefined && param.color != undefined )? param.color : "red"
+		let width = (param != undefined && param.width != undefined )? param.width : 3
+		let lineCap = (param != undefined && param.lineCap != undefined )? param.lineCap : "round"
+		let style = new Style({
+			stroke: new Stroke({
+				color: color,
+				width: width,
+				lineCap: lineCap
+			}),
+			zIndex: 0
+		})
+		vm.polyline.setStyle(style)
+	}
+
+	/**
+	 * 获取线标记端点坐标数组
+	 * @returns KMap.LngLat格式的经纬度数组集合
+	 */
+	getCoordinates() {
+    const vm = this
+		let points = []
+		let coordinates = vm.polyline.getGeometry().getCoordinates()
+		for(let i = 0 ; i < coordinates.length ; i ++)
+		{
+			let coordinate =  proj.toLonLat(coordinates[i]) //平面坐标转经纬度坐标
+			coordinate = Common.MapLngLat2KMapLngLat(coordinate)//Openlayers经纬度坐标转利通地图经纬度坐标
+			points.push(coordinate)
+		}
+		return points
+	}
+
+	/**
+	 * 显示线标记
+	 */
+	show() {
+    const vm = this
+		if(!vm.source.hasFeature(vm.polyline)) {
+			vm.source.addFeature(vm.polyline)
+		}
+	}
+
+	/**
+	 * 隐藏线标记
+	 */
+	hide() {
+    const vm = this
+		if(vm.source.hasFeature(vm.polyline)) {
+			vm.source.removeFeature(vm.polyline)
+		}
+	}
+
+	/**
+	 * 删除线标记
+	 */
+	remove() {
+    const vm = this
+		if(vm.source.hasFeature(vm.polyline)) {
+			vm.source.removeFeature(vm.polyline)
+		}
+	}
+
+	/**
+	 * 获取线标记叠加顺序
+	 * @param 线标记叠加顺序
+	 */
+	getZIndex() {
+    const vm = this
+		let index = vm.polyline.getStyle().getZIndex()
+		return index
+	}
+
+	/**
+	 * 设置线标记叠加顺序
+	 * @param index index值大的显示在上方,值相同时后加载的标记显示在上方,必填
+	 */
+	setZIndex(index) {
+    const vm = this
+		vm.polyline.getStyle().setZIndex(index)
+	}
+
+	/**
+	 * 地图视角缩放到线标记范围
+	 * @param duration 动画持续时间(单位:毫秒) 选填,默认0毫秒
+	 */
+	zoomToExtent(duration) {
+    const vm = this
+		duration = (duration)? duration : 0
+		let LonLatArray = vm.polyline.getGeometry().getCoordinates()
+		let extentBound = olExtent.boundingExtent(LonLatArray)
+		vm.map.getView().fit(extentBound,{
+			duration: duration
+		})
+	}
+
+	/**
+	 * 获取获取用户自定义属性
+	 * @param 用户自定义属性(支持数字,字符串,json格式)
+	 */
+	getExtData() {
+    const vm = this
+		let extData = vm.polyline.values_.properties
+		return extData
+	}
+
+	/**
+	 * 设置用户自定义属性
+	 * @param extData 用户自定义属性(支持数字,字符串,json格式),必填
+	 */
+	setExtData(extData) {
+    const vm = this
+		extData = (extData)? extData : null
+		if(extData != null) {
+			vm.polyline.values_.properties = extData
+		}
+	}
+
+	/**
+	 * 设置文本标记地图对象,传入null时,移除点标记,
+	 * 建议不使用此API,使用remove方法
+	 * @param map 传入null,移除点标记
+	 */
+	setMap(map) {
+    const vm = this
+		map = (map)? map : null;
+		if(map == null) {//当传入null时删除线标记
+			if(vm.source.hasFeature(vm.polyline)) {
+				vm.source.removeFeature(vm.polyline);
+			}
+		}
+	}
+
+	/**
+	 * 注册线标记事件
+	 * @param eventName 鼠标事件
+	 * "click":鼠标点击事件 "mousemove":鼠标悬停事件
+	 * @param callback 选中线标记时触发函数
+	 */
+	on(eventName,callback) {
+		let that = this
+    const vm = this
+		if(eventName == "pointermove"){
+			vm.map.on('pointermove',function(e) {
+				let pixel=vm.map.getEventPixel(e.originalEvent)
+				let feature=vm.map.forEachFeatureAtPixel(pixel,function (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)
+					}
+				}
+			})
+		}else{
+			vm.polyline.on(eventName,callback)
+		}
+	}
+
+	/**
+	 * 注销线标记事件(暂无该方法)
+	 */
+	off(eventName,callback) {}
+
+	/**
+	 * 线标记点击弹窗
+	 * @param param JSON对象,弹窗参数信息,必填
+	 * param.content 生成弹窗内容的回调函数,必填
+	 * param.offsetX X轴方向偏移量(正向右,负向左),选填参数,默认为0
+	 * param.offsetY Y轴方向偏移量(正向下,负向上),选填参数,默认为-25
+	 */
+	infoWindow(param){
+    const vm = this
+		let eventName = "click"
+		vm.polyline.on(eventName,function(e){
+			let lnglat = proj.toLonLat(e.event.coordinate)
+			lnglat = new LngLat(lnglat[0],lnglat[1])
+			let content = (param.content != undefined)? param.content : ""
+			let offsetX = (param.offsetX != undefined)? param.offsetX : ""
+			let offsetY = (param.offsetY != undefined)? param.offsetY : ""
+			let infoWindow = new InfoWindow({
+				type: "click",
+				position: lnglat,
+				content: content,
+				offsetX: offsetX,
+				offsetY: offsetY
+			})
+			infoWindow.open()
+		})
+  }
+}
+export default Polyline

+ 101 - 0
src/utils/ol-map/Popup.js

@@ -0,0 +1,101 @@
+import Overlay from 'ol/Overlay'
+import KBaseObject from './KBaseObject';
+import * as proj from 'ol/proj'
+class Popup extends KBaseObject{
+  constructor(lng,lat,url,offsetX,offsetY,width,height,mapInstance = null){
+    super(mapInstance)
+    const vm = this;
+    vm.position = [lng,lat];
+    vm.initPopup(lng,lat,url,offsetX,offsetY,width,height);
+
+  }
+  initPopup(lng,lat,url,offsetX,offsetY,width,height){
+    //创建popup的容器
+    const vm = this;
+    vm.popupContainer = document.createElement("div");
+    vm.popupContainer.style.position = "absolute";
+    vm.popupContainer.style.width = width + "px";
+    vm.popupContainer.style.height = height + "px";
+    vm.popupContainer.style.top = "0px";
+    vm.popupContainer.style.left = "0px";
+    vm.popupContainer.style.zIndex = 1;
+    // vm.popupContainer.style.display = "none";
+    vm.popupContainer.style.background = "url(" + url + ") no-repeat";
+    vm.popupContainer.style.backgroundSize = "100% 100%";
+    // vm.popupContainer.innerHTML = "点击查看";
+    //创建popup 
+    vm.popup = new Overlay({
+      element: vm.popupContainer,
+      positioning: 'center-center',
+      stopEvent: false,
+      position: proj.fromLonLat([lng,lat]),
+      offset: [offsetX,offsetY]
+    });
+    vm.mapInstance.map.addOverlay(vm.popup);    
+  }
+  setPostionAnimate(lng,lat,duration = 1000){
+    const vm = this;
+    let curLng = vm.position[0];
+    let curLat = vm.position[1];
+    let nextLng  = lng;
+    let nextLat  = lat;
+    // 计算经纬度的中间值差距
+    let latDiff = (nextLng - curLng) / 100;
+    let lngDiff = (nextLat - curLat) / 100;
+    let j = 0;
+    // 在当前坐标和下一个坐标之间插入 100 个中间值的经纬度
+    let timer = setInterval(function (){
+        let x = curLng + j * latDiff;
+        let y = curLat + j * lngDiff;
+        vm.popup.setPosition(proj.fromLonLat([x,y]));
+        j++;
+        if(j == 100){
+          clearInterval(timer)
+        }
+    }, duration / 100)
+
+  }
+  setPosition(x,y){
+    this.popup.setPosition(proj.fromLonLat([x,y]));
+  }
+
+  insertMiddleValues(coordinates) {
+    // 初始化一个新的数组
+    let result = [];
+
+    // 循环遍历所有的坐标
+    for (let i = 0; i < coordinates.length - 1; i++) {
+      // 获取当前坐标和下一个坐标
+      let current = coordinates[i];
+      let next = coordinates[i + 1];
+
+      // 计算经纬度的中间值差距
+      let latDiff = (next[0] - current[0]) / 200;
+      let lngDiff = (next[1] - current[1]) / 200;
+
+      // 将当前坐标添加到结果数组
+      result.push(current);
+
+      // 在当前坐标和下一个坐标之间插入 200 个中间值的经纬度
+      for (let j = 1; j < 200; j++) {
+        let lat = current[0] + j * latDiff;
+        let lng = current[1] + j * lngDiff;
+        result.push([lat, lng]);
+      }
+    }
+    // 添加最后一个坐标到结果数组
+    result.push(coordinates[coordinates.length - 1]);
+
+    return result;
+  }
+
+  remove(){
+    const vm = this;
+    if(vm.popup){
+      vm.mapInstance.map.removeOverlay(vm.popup);
+      vm.popup = null;
+    }
+  }
+}
+
+export default Popup;

+ 39 - 0
src/utils/ol-map/Size.js

@@ -0,0 +1,39 @@
+/**
+ * @description KMap.Size 大小类
+ */
+class Size{
+  /**
+   * @param {number} width 宽度
+   * @param {number} height 高度
+   * @constructor
+   */
+  constructor(width,height){
+    let mapSize = [Number(width),Number(height)]
+    this.size = mapSize
+  }
+
+  /**
+  * @description 获得宽度
+  * @returns {number} 宽度
+  */
+  getWidth() { 
+    return this.size[0]
+  }
+
+  /**
+  * @description 获取高度
+  * @returns {number} 高度
+  */
+  getHeight() { 
+    return this.size[1]
+  }
+
+  /**
+  * @description 以字符串形式返回尺寸大小对象
+  * @returns {number} 像素尺寸字符串,用逗号连接
+  */
+  toString() { 
+    return this.size[0] + "," + this.size[1]
+  }
+}
+export default Size

+ 87 - 0
src/utils/ol-map/VTLayer.js

@@ -0,0 +1,87 @@
+import Layer from 'ol/layer/VectorTile';
+import Vector from 'ol/layer/Vector';
+import VectorSource from 'ol/source/Vector';
+import Source from 'ol/source/VectorTile';
+import Common from './Common';
+import MVT from 'ol/format/MVT';
+import {createXYZ} from 'ol/tilegrid'
+import KBaseObject from './KBaseObject'
+/**
+ * @description KMap.VectorLayer 矢量图层
+ */
+class VTLayer extends KBaseObject{
+  /**
+   * @param {string} name 图层名称
+   * @param {string} zIndex 图层层级
+  */
+  constructor(url,options,zIndex,callback,mapInstance = null){
+    super(mapInstance)
+    const vm = this;
+    let styleFunVT = options.styleFunVT;
+    let styleFunV = options.styleFunV;
+    let funThis = options.funThis;
+    var source = new Source({
+        format: new MVT(),
+        url: url,
+        tileGrid: createXYZ({
+            tileSize: options.tileSize || 4096 // 瓦片大小
+        }),
+        tileLoadFunction:(tile, url)=>{
+            tile.setLoader(function(extent, resolution, projection) {
+                fetch(url).then(function(response) {
+                    response.arrayBuffer().then(function(data) {
+                        const format = tile.getFormat();
+                        const features = format.readFeatures(data, {
+                            extent: extent,
+                            featureProjection: projection
+                        });
+                        tile.setFeatures(features);
+                        callback(features);
+                    });
+                });
+            });
+        }
+    });
+    let ShowLevel = Common.ShowLevel
+    let minZoom = ShowLevel[0]
+    let maxZoom = ShowLevel[1]
+    if(options && options.minZoom){
+      minZoom = options.minZoom
+    }
+    if(options && options.maxZoom){
+      maxZoom = options.maxZoom
+    }
+    let layer = new Layer({
+      source: source,
+      zIndex: zIndex,
+      minZoom: minZoom,
+      maxZoom: maxZoom,
+      style:(evt)=>{
+        return styleFunVT.call(funThis,evt);
+      }
+    })
+    this.layer = layer
+    this.source = source,
+    this.vLayer = this.initVectorLayer(styleFunV,funThis,minZoom,maxZoom)
+  }
+  setMaxZoom(maxZoom){
+    this.layer.setMaxZoom(maxZoom);
+  }
+  setMinZoom(minZoom){
+    this.layer.setMinZoom(minZoom);
+  }
+  initVectorLayer(styleFunV,funThis,minZoom,maxZoom){
+    let layer = new Vector({
+        source:new VectorSource({}),
+        style:(evt)=>{
+            return styleFunV.call(funThis,evt);
+        },
+        minZoom: minZoom,
+        maxZoom: maxZoom,
+        zIndex:101
+    })
+    return layer;
+  }
+}
+
+export default VTLayer

+ 98 - 0
src/utils/ol-map/VectorLayer.js

@@ -0,0 +1,98 @@
+import Layer from 'ol/layer/Vector'
+import Source from 'ol/source/Vector'
+import Common from './Common'
+import Select from 'ol/interaction/Select.js';
+import {singleClick} from 'ol/events/condition'
+import * as olEvents from 'ol/events';
+// import {OrderFunction} from 'ol/render';
+/**
+ * @description KMap.VectorLayer 矢量图层
+ */
+class VectorLayer {
+  /**
+   * @param {string} name 图层名称
+   * @param {string} zIndex 图层层级
+  */
+  constructor(name,zIndex,options){
+    let source = new Source({
+    })
+    let ShowLevel = Common.ShowLevel
+    let minZoom = ShowLevel[0]
+    let maxZoom = ShowLevel[1]
+    let style = null;
+    let visible = true
+    if(options && options.source){
+      source = options.source
+    }
+    if(options && options.minZoom){
+      minZoom = options.minZoom
+    }
+    if(options && options.maxZoom){
+      maxZoom = options.maxZoom
+    }
+    if(options && options.style){
+      style = options.style
+    }
+    if(options && options.visible){
+      visible = options.visible
+    }
+    let layer = new Layer({
+      source:source,
+      name:name,
+      visible: visible,
+      zIndex:zIndex,
+      minZoom:minZoom,
+      maxZoom:maxZoom,
+    })
+    if(style){
+      layer.setStyle(style)
+    }
+    this.layer = layer
+    this.source = source
+  }
+  setMaxZoom(maxZoom){
+    this.layer.setMaxZoom(maxZoom);
+  }
+  setMinZoom(minZoom){
+    this.layer.setMinZoom(minZoom);
+  }
+  addFeature(feature){
+    this.source.addFeature(feature)
+  }
+  refresh(){
+    this.source.refresh()
+  }
+  getFeatureById(id){
+    return this.source.getFeatureById(id)
+  }
+  addSingleSelect(callback,map,style){
+    let option = {
+        condition:singleClick,
+        layers:[this.layer],
+        multi:false
+    };
+    if(style){
+      option["style"] = style
+    }
+    this.singleSelect = new Select(option)
+    this.singleSelect.on("select",callback)
+    map.addInteraction(this.singleSelect)
+  }
+  addToggleSelect(callback,map,style){
+    let option = {
+      condition:singleClick,
+      toggleCondition: singleClick,
+      layers:[this.layer],
+      multi:true
+    };
+    if(style){
+      option["style"] = style
+    }
+    this.toggleSelect = new Select(option)
+    this.toggleSelect.on("select",callback)
+    map.addInteraction(this.toggleSelect)
+  }
+
+}
+
+export default VectorLayer

+ 211 - 0
src/utils/ol-map/VectorStyle.js

@@ -0,0 +1,211 @@
+
+import LTBaseObject from './KBaseObject'
+import XYZ from 'ol/source/XYZ'
+import Tile from 'ol/layer/Tile'
+import Common from './Common'
+import Style from "ol/style/Style";
+import Fill from "ol/style/Fill";
+import Stroke from "ol/style/Stroke";
+import Text from "ol/style/Text";
+import Icon from "ol/style/Icon";
+import Circle from "ol/style/Circle";
+import Photo from "ol-ext/style/Photo";
+import config from "../../api/config";
+/**
+ * @description 样式类
+*/
+class VectorStyle{
+	constructor() {
+		this.polymerImgLayerStyleCache = {}
+		this.getPointTextStyleCache = {}
+		this.resize = "?x-oss-process=image/resize,w_400";
+	}
+	
+	getPolymerImgLayerStyle(feature) {
+		let that = this
+		// let zoom = that.kmap.view.getZoom();
+		// let r = getRadius(zoom);
+		let r = 25;
+		let img = feature.get("src");
+		let count = feature.get("count");
+		let k = img + r;
+		if (img && img != "") {
+			let style = that.polymerImgLayerStyleCache[k];
+			if (!style) {
+				style = new Style({
+					image: new Photo({
+						src: config.base_img_url2 + img + that.resize,
+						radius: r,
+						shadow: 0,
+						crop: true,
+						kind: "square",
+						onload: function () {
+							that.polymerImgLayer.layer.changed();
+						},
+						displacement: [0, 30 + 15],
+						stroke: new Stroke({
+							width: 3,
+							color: "#fdfcfc",
+						}),
+					}),
+				});
+				that.polymerImgLayerStyleCache[k] = style;
+			}
+			return [style, that.getCount(count + "")];
+		} else {
+			return that.getPointSimpleStyle(10, "#00000000", "#00000000", 1);
+		}
+	}
+	
+	getGardenReportStyle(gardenPointLayer) {
+		let style1 = new Style({
+			image: new Photo({
+				// src: require("@/assets/img/old_mini/gybg.png"),
+				radius: 36,
+				shadow: 0,
+				crop: false,
+				onload: function () {
+					gardenPointLayer.layer.changed();
+				},
+				displacement: [70, 0],
+				stroke: new Stroke({
+					width: 2,
+					color: "#fdfcfc00",
+				}),
+			}),
+		});
+		return style1
+	}
+
+	getLineStyle(fillColor, strokeColor, strokeWidth){
+		let style = new Style({
+			stroke: new Stroke({
+				color: strokeColor,
+				width: strokeWidth || 1,
+			}),
+		});
+		return style;
+	}
+
+	getPolygonStyle(fillColor, strokeColor, strokeWidth){
+		let style = new Style({
+			fill: new Fill({
+				color: fillColor
+			}),
+			stroke: new Stroke({
+				color: strokeColor,
+				width: strokeWidth || 1,
+			}),
+		});
+		return style;
+	}
+	getPointStyle(src, scale, anchor, text, font, textColor){
+		let textObj = null;
+		text &&	(textObj = new Text({
+			text:text,
+			stroke: new Stroke({
+				color: textColor,
+				width: 1,
+			}),
+			fill: new Fill({
+				color: textColor,
+			}),
+			font:font
+		}))
+		let style = new Style({
+			image: new Icon({
+				text:textObj,
+				src:src,
+				scale:scale,
+				anchor:anchor,
+			})
+		});
+		return style
+	}
+
+	/**
+	 *
+	 * @param radius 半径
+	 * @param fillColor 填充颜色
+	 * @param strokeColor 边框颜色
+	 * @param strokeWidth 边框宽度
+	 * @returns {Style}
+	 */
+	getPointSimpleStyle(radius, fillColor, strokeColor, strokeWidth){
+		let style = new Style({
+			image: new Circle({
+				radius: radius,                             // 半径
+				stroke: new Stroke({           // 边界样式
+					color: strokeColor,                    // 边界颜色
+					width: strokeWidth                           // 边界宽度
+				}),
+				fill: new Fill({               // 填充样式
+					color: fillColor                       // 填充颜色
+				})
+			})
+		});
+		return style
+	}
+
+	/**
+	 *
+	 * @param text
+	 * @param fillColor 填充颜色
+	 * @param strokeColor 边框颜色
+	 * @param strokeWidth 边框宽度
+	 * @returns {Style}
+	 */
+	getPointTextStyle(text, fillColor, strokeColor, strokeWidth, fontSize){
+		if(this.getPointTextStyleCache[text]){
+			return this.getPointTextStyleCache[text]
+		}
+		let style = new Style({
+			text: new Text({
+				text:text,
+				stroke: new Stroke({
+					color: strokeColor,
+					width: strokeWidth,
+				}),
+				fill: new Fill({
+					color: fillColor,
+				}),
+				font: fontSize+"px sans-serif"
+			}),
+		});
+		this.getPointTextStyleCache[text] = style
+		return style
+	}
+
+	/**
+	 *
+	 * @param text
+	 * @returns {Style}
+	 */
+	getCount(text){
+		let style = new Style({
+			text: new Text({
+				text:text,
+				stroke: new Stroke({
+					color: "#ffffff",
+					width: 1,
+				}),
+				fill: new Fill({
+					color: "#ffffff",
+				}),
+				font: "24rpx sans-serif",
+				backgroundFill: new Fill({
+						color: "#0c0c0c",
+				}),
+				backgroundStroke: new Stroke({
+					color: "#cccccc",
+					width: 1,
+				}),
+				offsetX:20,
+				offsetY:-70	,
+				padding:[4, 5, 0, 5]
+			}),
+		});
+		return style
+	}
+}
+export default VectorStyle

+ 327 - 0
src/utils/ol-map/WMSLayer.js

@@ -0,0 +1,327 @@
+import Image from 'ol/layer/Image'
+import ImageWMS from 'ol/source/ImageWMS'
+import {GeoJSON,WFS} from 'ol/format'
+import * as filter from 'ol/format/filter'
+import * as extent from 'ol/extent'
+import KBaseObject from './KBaseObject'
+import Projection from 'ol/proj/Projection'
+import TileWMS from 'ol/source/TileWMS'
+import Tile from 'ol/layer/Tile'
+/**
+ * @description LTMap.WMSLayer WMS图层类
+*/
+class WMSLayer extends KBaseObject{
+	/**
+	 * @description 构造函数
+	 * @param {String} url wms图层服务地址
+	 * @param {LTMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof WMSLayer
+	 */
+	constructor(url,layerName,mapInstance = null){
+    super(mapInstance)
+		const vm = this
+		vm.format = "image/png"
+		vm.initImageLayer(url,layerName)
+		// vm.initTileLayer(url,layerName)
+  }
+	
+	/**
+		* @description 初始化ImageLayer
+		* @memberof WMSLayer
+		*/
+	initImageLayer(url,layerName){
+		const vm = this
+
+		var projection = new Projection({
+			code: 'EPSG:4326',
+			units: 'degrees',
+			axisOrientation: 'neu',
+			global: true
+		})
+		
+    vm.source = new ImageWMS({
+      url: url,
+			ratio: 1,
+      params: {
+				'FORMAT':vm.format,
+        "LAYERS": layerName,
+				"STYLES": '',
+				'VERSION': '1.1.1',
+				"exceptions": 'application/vnd.ogc.se_inimage'
+      },
+      serverType: 'geoserver',
+      crossOrigin: 'anonymous',
+			projection:projection
+    })
+
+		vm.layer = new Image({
+			source:vm.source
+		})
+
+    vm.state = true
+
+    //地图加载WMS图层
+    vm.map.addLayer(vm.layer)
+	}
+
+	/**
+	 * @description 切片加载wms图层
+	 * @param {*} url
+	 * @param {*} layerName
+	 * @memberof WMSLayer
+	 */
+	initTileLayer(url,layerName){
+		const vm = this
+
+		var projection = new Projection({
+			code: 'EPSG:4326',
+			units: 'degrees',
+			axisOrientation: 'neu',
+			global: true
+		})
+
+		vm.source = new TileWMS({
+			url: url,
+			params: {
+				'FORMAT': vm.format, 
+				'VERSION': '1.1.1',
+				tiled: true,
+				"STYLES": '',
+				"LAYERS": layerName,
+				"exceptions": 'application/vnd.ogc.se_inimage',
+				// tilesOrigin: -124.73142200000001 + "," + 24.955967
+			},
+			projection:projection
+		})
+
+		vm.layer = new Tile({
+			source:vm.source
+		})
+
+		vm.map.addLayer(vm.layer)
+	}
+
+  /**
+	* @description 添加WMS图层到地图
+	* @memberof WMSLayer
+	*/
+  add(){
+		const vm = this
+		if(!vm.state) {
+			vm.map.addLayer(vm.layer)
+			vm.state = true
+		}
+	}
+
+	/**
+	 * @description 从当前地图中移除WMS图层
+	 * @memberof WMSLayer
+	 */
+	remove() {
+		const vm = this
+		if(vm.state) {
+			vm.map.removeLayer(vm.layer)
+			vm.state = false
+		}
+	}
+
+	/**
+	 * @description 显示WMS图层数据
+	 * @memberof WMSLayer
+	 */
+	show(){
+		const vm = this
+		if(vm.state) {
+			vm.layer.setVisible(true)
+		}
+	}
+
+	/**
+	 * @description 隐藏WMS图层数据
+	 * @memberof WMSLayer
+	 */
+	hide(){
+		const vm = this
+		if(vm.state) {
+			vm.layer.setVisible(false)
+		}
+	}
+
+	/**
+	 * @description 更新WMS图层内容
+	 * @param {String} data 属性过滤参数(例如:RoadNo=11),选填,不传值默认加载全部数据
+	 * @memberof WMSLayer
+	 */
+	update(data) {
+		const vm = this
+		if(data!=null && data!="" && data!=undefined && state) {
+			vm.source.updateParams({
+				"CQL_FILTER": data
+			})
+		}
+		else{
+			vm.source.updateParams({
+				"CQL_FILTER": null
+			})
+		}
+	}
+
+	/**
+	 * @description 缩放到过滤后的地图要素对应范围
+	 * @memberof WMSLayer
+	 */
+	zoomTo() {
+    const vm = this
+		//组装查询过滤条件
+		let cqlfilter = vm.source.getParams().CQL_FILTER
+		if(cqlfilter == null){
+			//缩放到图层范围
+		}
+		else{
+			let cqlfilters = cqlfilter.split("=")
+			//实现思路为通过WFS进行属性查询,查找到要素后再缩放到该要素
+			let wfsurl = url.substr(0, url.lastIndexOf("/") + 1 ) + "wfs"
+			let featureRequest = new WFS().writeGetFeature({
+        //srsName: 'EPSG:3857',
+        //featureNS: 'http://openstreemap.org',
+        //featurePrefix: 'skyline',
+        featureTypes: [layer],
+        outputFormat: 'application/json',
+        filter: filter.equalTo(cqlfilters[0],cqlfilters[1])
+			})
+			let myfilter = WFS.writeFilter(filter.equalTo(cqlfilters[0],cqlfilters[1]))
+			$.ajax({
+				type: 'get',
+				url: wfsurl,
+				data: {
+					service: 'WFS',
+          request: 'GetFeature',
+          typeNames: layer,//查询图层
+          filter:new XMLSerializer().serializeToString(myfilter),//查询条件
+          outputFormat: 'application/json'
+				},
+				async : false,
+				success: function(res) {
+					let features = new GeoJSON().readFeatures(res)
+					  //let coordinateArray = new Array();
+					  let extentBound = new extent.createEmpty()
+						for(let i=0; i<features.length; i++) {
+							/*let coordinates =  features[i].getGeometry().getCoordinates();//由多个分部构成,与Polyline函数生成的结构不一致
+							for(j=0; j<coordinates.length; j++) {
+								let pathcoordinates = coordinates[j];
+								for(k = 0 ; k < pathcoordinates.length ; k ++)
+								coordinateArray.push(pathcoordinates[k]);
+							}*/
+							extentBound = new extent.extend(extentBound,features[i].getGeometry().getExtent());
+						}
+						//let extentBound = new extent.boundingExtent(coordinateArray);
+						if(features.length > 0){
+							vm.map.getView().fit(extentBound,{
+								duration: 1
+							})
+						}
+				},
+				error: function(e) {
+					console.log("failed")
+				}
+			})
+			/*fetch(wfsurl, {
+				method: 'POST',
+				body: new XMLSerializer().serializeToString(featureRequest)
+			}).then(function(response) {
+				return response.json();
+			}).then(function(json) {
+				let features = new GeoJSON().readFeatures(json);
+				let extentBound = new extent.createEmpty();
+				for(let i=0; i<features.length; i++) {
+					extentBound = new extent.extend(extentBound,features[i].getGeometry().getExtent());
+				}
+				if(features.length > 0)
+				{
+					map.getView().fit(extentBound,{
+						duration: 1
+					});
+				}
+			});*/
+		}
+	}
+	
+	/**
+	 *@description 根据经纬度坐标查询坐标点所在位置的要素
+	 *@param coordinate LTMap.LngLat格式的经纬度 必填
+	 *@returns 查询到要素的结果集合,json数组格式,json中包含对应要素的所有字段名和字段值
+	*/
+	getFeaturesByCoor(coordinate){
+    const vm = this
+		//将经纬度坐标转平面坐标
+		coordinate = vm.mapInstance.lnglatToPixel(coordinate)
+    //将LT平面坐标转换为OL的平面坐标
+		coordinate = Common.LTMapPixel2MapPixel(coordinate)
+		let resolution = vm.mapInstance.getResolution()
+		let projection = vm.mapInstance.getProjection()
+    let url = vm.source.getGetFeatureInfoUrl(coordinate, resolution, projection,
+    {'INFO_FORMAT': 'application/json', 'FEATURE_COUNT': 10})
+      let geojsonFormat = new GeoJSON({
+    	defaultDataProjection: "EPSG:3857"
+		})
+    let result = []
+		if(url) {
+			$.ajax({
+				type: 'get',
+				url: url,
+				async : false,
+				success: function(res) {
+          //获取所有的要素
+					let features = geojsonFormat.readFeatures(res)
+					let jsonDatas = []
+					for(let i = 0 ; i < features.length ; i ++){
+						let jsonData = {}
+					    for(let key in features[i].getProperties()){
+						    if (key == 'geometry'){
+	                continue
+	              }
+	              jsonData[key] = features[i].get(key)
+					    }
+					    jsonDatas.push(jsonData)
+					}
+					result = jsonDatas
+				},
+				error:function(e) {
+					console.log("faile")
+				}
+			})
+		}
+		return result
+	}
+
+	/**
+	 *@description 设置鼠标悬停在元素上时的样式
+	 *@param {String} cursorStyle 鼠标样式("default"默认指针,"pointer"小手,
+   *"move"移动指针,"text"文本指针,
+   *"wait"等待状态,"help"帮助)
+   *必填,由于跨域问题,该方法存在问题
+	*/
+	setFeatureCursor(cursorStyle){
+    const vm = this
+		cursorStyle = (cursorStyle == undefined)?"default":cursorStyle
+		let mapContainer = vm.map.getTargetElement()
+		let defaultCursor = mapContainer.style.cursor
+		vm.map.on("pointermove",function(e){
+			//let features = map.forEachFeatureAtPixel(e.pixel,function(feature) { return feature; })
+			if (e.dragging){
+		    return
+		  }
+			let pixel = vm.map.getEventPixel(e.originalEvent)
+	    let hit = vm.map.forEachLayerAtPixel(pixel,function(){
+	      return true
+	    })
+			if(hit){
+				mapContainer.style.cursor = cursorStyle
+			}
+			else{
+				mapContainer.style.cursor = defaultCursor
+			}
+		})
+	}
+}
+export default WMSLayer

+ 111 - 0
src/utils/ol-map/WMTSLayer.js

@@ -0,0 +1,111 @@
+import LTBaseObject from './KBaseObject'
+import TileLayer from 'ol/layer/Tile'
+import sourceWMTS from 'ol/source/WMTS';
+import WMTSTileGrid from 'ol/tilegrid/WMTS';
+import * as olExtent from 'ol/extent';
+/**
+ * @description KMap.WMTSLayer WMS图层类
+*/
+class WMTSLayer extends LTBaseObject{
+	/**
+	 * @description 构造函数
+	 * @param {String} url wms图层服务地址
+	 * @param {LTMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof WMTSLayer
+	 */
+	constructor(wmtsData,projection,mapInstance = null){
+		super(mapInstance)
+		const vm = this;
+		vm.initLayer(wmtsData,projection)
+	}
+
+	/**
+		* @description 初始化ImageLayer
+		* @memberof WMTSLayer
+		*/
+	initLayer(wmtsData,projection){
+		const vm = this;
+		let projectionExtent = projection.getExtent();
+    let size = olExtent.getWidth(projectionExtent) / 256;
+    let resolutions = new Array(19);
+    let matrixIds = new Array(19);
+    for (var z = 1; z < 19; ++z) {
+        // generate resolutions and matrixIds arrays for this WMTS
+        resolutions[z] = size / Math.pow(2, z);
+        matrixIds[z] = z;
+    }
+
+    let source = new sourceWMTS({
+        url: wmtsData.url,
+        layer: wmtsData.layer,
+        matrixSet: wmtsData.matrixSet,
+        format: 'tiles',
+        projection: projection,
+        tileGrid: new WMTSTileGrid({
+            origin: olExtent.getTopLeft(projectionExtent),
+            resolutions: resolutions,
+            matrixIds: matrixIds
+        }),
+        style: 'default',
+        wrapX: true
+    })
+    let layer = new TileLayer({
+        zIndex:wmtsData.zIndex,
+        source: source,
+        // visible: false,
+    });
+    layer.id = wmtsData.layer+"_"+wmtsData.matrixSet;
+		vm.layer = layer;
+		vm.source = source;
+		vm.map.addLayer(vm.layer)
+    return layer;
+    //地图加载WMS图层
+	}
+
+  /**
+	* @description 添加WMS图层到地图
+	* @memberof WMTSLayer
+	*/
+  add(){
+		const vm = this
+		if(!vm.state) {
+			vm.map.addLayer(vm.layer)
+			vm.state = true
+		}
+	}
+
+	/**
+	 * @description 从当前地图中移除WMS图层
+	 * @memberof WMTSLayer
+	 */
+	remove() {
+		const vm = this
+		if(vm.state) {
+			vm.map.removeLayer(vm.layer)
+			vm.state = false
+		}
+	}
+
+	/**
+	 * @description 显示WMS图层数据
+	 * @memberof WMTSLayer
+	 */
+	show(){
+		const vm = this
+		if(vm.state) {
+			vm.layer.setVisible(true)
+		}
+	}
+
+	/**
+	 * @description 隐藏WMS图层数据
+	 * @memberof WMTSLayer
+	 */
+	hide(){
+		const vm = this
+		if(vm.state) {
+			vm.layer.setVisible(false)
+		}
+	}
+}
+export default WMTSLayer

+ 108 - 0
src/utils/ol-map/XYZLayer.js

@@ -0,0 +1,108 @@
+
+import LTBaseObject from './KBaseObject'
+import XYZ from 'ol/source/XYZ'
+import Tile from 'ol/layer/Tile'
+import Common from './Common'
+/**
+ * @description LTMap.XYZLayer XYZ图层类
+*/
+class XYZLayer extends LTBaseObject{
+	/**
+	 * @description 构造函数
+	 * @param {String} url XYZ图层服务地址
+	 * @param {LTMap.Map} [mapInstance=null] map对象,单地图的时候可不传,多地图时候需要传
+	 * @memberof XYZLayer
+	 */
+	constructor(url,options,zIndex,mapInstance = null){
+    super(mapInstance)
+		const vm = this
+		vm.initXYZLayer(url,options,zIndex)
+  }
+	/**
+	 * @description 切片加载XYZ图层
+	 * @param {*} url
+	 * @param {*} layerName
+	 * @memberof XYZLayer
+	 */
+	initXYZLayer(url,options,zIndex) {
+		var minZoom = Common.BaseLayerZoom[0];
+		var maxZoom = Common.BaseLayerZoom[1];
+		if(options && options.minZoom != undefined){
+			minZoom = options.minZoom
+		}
+		if(options && options.maxZoom != undefined){
+			maxZoom = options.maxZoom
+		}
+		const vm = this
+		vm.source = new XYZ({
+      url : url
+    })
+
+		vm.layer = new Tile({
+			source:vm.source,
+			maxZoom:maxZoom,
+			minZoom:minZoom,
+			zIndex:zIndex
+		})
+
+		vm.map.addLayer(vm.layer)
+	}
+	setProperty(properties){
+		const vm = this;
+		vm.properties = properties;
+	}
+	getProperty(){
+		const vm = this;
+		return vm.properties?vm.properties:null;
+	}
+  /**
+	* @description 添加XYZ图层到地图
+	* @memberof XYZLayer
+	*/
+  add(){
+		const vm = this
+		if(!vm.state) {
+			vm.map.addLayer(vm.layer)
+			vm.state = true
+		}
+	}
+
+	/**
+	 * @description 从当前地图中移除XYZ图层
+	 * @memberof XYZLayer
+	 */
+	remove() {
+		const vm = this
+		if(vm.state) {
+			vm.map.removeLayer(vm.layer)
+			vm.state = false
+		}
+	}
+
+	/**
+	 * @description 显示XYZ图层数据
+	 * @memberof XYZLayer
+	 */
+	show(){
+		const vm = this
+		vm.layer.setVisible(true)
+	}
+
+	/**
+	 * @description 隐藏XYZ图层数据
+	 * @memberof XYZLayer
+	 */
+	hide(){
+		const vm = this
+		vm.layer.setVisible(false)
+	}
+
+	/**
+	 * @description 缩放到过滤后的地图要素对应范围
+	 * @memberof XYZLayer
+	 */
+	zoomTo() {
+    const vm = this
+	}
+}
+export default XYZLayer

+ 88 - 0
src/utils/ol-map/css/KMap.css

@@ -0,0 +1,88 @@
+.ol-mouse-position-KMap{
+  color:#66b166;
+  position: absolute;
+  font-weight: 500;
+  bottom:8px;
+  right:8px;
+}
+/*鹰眼控件展开时的控件外边框*/
+.myOverview:not(.ol-collapsed){
+  border:1px solid black;
+}
+/*鹰眼控件地图容器边框样式*/
+.myOverview .ol-overviewmap-map{
+  border:none;
+  width:200px;
+  height: 100px;
+}
+/*鹰眼控件中显示当前窗口区域的边框样式*/
+.myOverview .ol-overviewmap-box{
+  border:2px solid red;
+}
+ /*鹰眼控件展开时其控件按钮图标的样式*/
+.myOverview:not(.ol-collapsed) button{
+  bottom:auto;
+  left:auto;
+  right:1px;
+  top:1px;
+}
+.ol-overviewmap{
+  left: auto !important;
+  right:0.5em;
+  bottom: 0.5em;
+}
+/*2D视图中添加自定义视图旋转控制器的样式*/
+.brightmap2d-rotate-control-custom {
+  position: absolute;
+  bottom: 90px;
+  right: 9px;
+  width: 52px;
+  height: 54px;
+  background: url(./rotate.png) 0% 0% / 266px no-repeat;
+}
+
+.center2d-button-custom {
+  position: absolute;
+  outline: none;
+  border: none;
+  background: url(./rotate.png) -56px -4px / 266px no-repeat;
+  cursor: pointer;
+  left: 19px;
+  top: 4px;
+  width: 14px;
+  height: 44px;
+  transform: rotate(0deg);
+}
+
+.right2d-button-custom {
+  position: absolute;
+  outline: none;
+  border: none;
+  background: url(./rotate.png) -75px -5px / 266px no-repeat;
+  right: 2px;
+  top: 5px;
+  width: 15px;
+  height: 42px;
+  transform: scaleX(-1);
+}
+
+.left2d-button-custom {
+  position: absolute;
+  outline: none;
+  border: none;
+  background: url(./rotate.png) -75px -5px / 266px no-repeat;
+  left: 2px;
+  top: 5px;
+  width: 15px;
+  height: 42px;
+}
+
+.left2d-button-custom:hover {
+  cursor: pointer;
+  background: url(./rotate.png) -89px -5px / 266px no-repeat;
+}
+
+.right2d-button-custom:hover {
+  cursor: pointer;
+  background: url(./rotate.png) -89px -5px / 266px no-repeat;
+}

BIN
src/utils/ol-map/css/rotate.png


+ 123 - 0
src/utils/ol_common.js

@@ -0,0 +1,123 @@
+import sourceWMTS from 'ol/source/WMTS';
+import WMTSTileGrid from 'ol/tilegrid/WMTS';
+import * as olExtent from 'ol/extent';
+import TileLayer from 'ol/layer/Tile';
+import XYZ from 'ol/source/XYZ.js';
+import {WKT} from "ol/format";
+import BufferOp from 'jsts/org/locationtech/jts/operation/buffer/BufferOp.js'
+import {OL3Parser} from "jsts/org/locationtech/jts/io"
+import * as geom from "ol/geom";
+import Feature from "ol/Feature";
+let jstsParser = new OL3Parser();
+jstsParser.inject(geom.Point, geom.LineString, geom.LinearRing, geom.Polygon, geom.MultiPoint, geom.MultiLineString, geom.MultiPolygon);
+
+
+
+//Code To RGB
+function CodeToRGB(code){
+    let result = [];
+    result.push(parseInt(code.substring(1, 3), 16));
+    result.push(parseInt(code.substring(3, 5), 16));
+    result.push(parseInt(code.substring(5) , 16));
+    return result;
+}
+
+
+
+//创建图层(WMTS方式)
+function createGDWmts(){
+    let AMapLayer = new TileLayer({
+        source: new XYZ({
+            url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
+        }),
+        visible: false,
+    });
+    return AMapLayer;
+}
+
+
+function crtLayerWMTS(wmtsData, opacity, projection){
+    let projectionExtent = projection.getExtent();
+    let size = olExtent.getWidth(projectionExtent) / 256;
+    let resolutions = new Array(19);
+    let matrixIds = new Array(19);
+    for (var z = 1; z < 19; ++z) {
+        // generate resolutions and matrixIds arrays for this WMTS
+        resolutions[z] = size / Math.pow(2, z);
+        matrixIds[z] = z;
+    }
+
+    let source = new sourceWMTS({
+        url: wmtsData.url,
+        layer: wmtsData.layer,
+        matrixSet: wmtsData.matrixSet,
+        format: 'tiles',
+        projection: projection,
+        tileGrid: new WMTSTileGrid({
+            origin: olExtent.getTopLeft(projectionExtent),
+            resolutions: resolutions,
+            matrixIds: matrixIds
+        }),
+        style: 'default',
+        wrapX: true
+    })
+
+    let layer = new TileLayer({
+        zIndex:wmtsData.zIndex,
+        source: source,
+        visible: false,
+    });
+    layer.id = wmtsData.layer+"_"+wmtsData.matrixSet;
+    return layer;
+}
+
+function wktCastGeom(wkt){
+    return new WKT().readGeometry(wkt);
+}
+
+function fit(wkt,view,zoom){
+    let extent = wktCastGeom(wkt).getExtent()
+    extent[0] = extent[0] / zoom;
+    extent[1] = extent[1] / zoom;
+    extent[2] = extent[2] * zoom;
+    extent[3] = extent[3] * zoom;
+    view.fit(extent,{duration:700})
+}
+
+function fitExtent(extent, view, zoom){
+    let extent2 = [];
+    extent2[0] = extent[0] / zoom;
+    extent2[1] = extent[1] / zoom;
+    extent2[2] = extent[2] * zoom;
+    extent2[3] = extent[3] * zoom;
+    view.fit(extent2,{duration:700})
+}
+
+const fitBuffer = (pointWkt, map, meter)=>{
+    let geom = new WKT().readGeometry(pointWkt);
+    let degree = meter / (2 * Math.PI * 6371004) * 360;
+    geom = jstsParser.write(BufferOp.bufferOp(jstsParser.read(geom), degree, 8))
+    map.getView().fit(geom,{duration: 1000})
+}
+
+const newAreaFeature = (data,fieldGeom)=>{
+    let geom = new WKT().readGeometry(data[fieldGeom || "geom"])
+    let feature = new Feature({
+        geometry: geom
+    });
+    feature.set("nodeType","area");
+    feature.setId(data.id)
+    for(let key in data){
+        if(key != "geom"){
+            feature.set(key,data[key])
+        }
+    }
+    return feature;
+}
+
+
+export{
+    crtLayerWMTS,CodeToRGB,createGDWmts,wktCastGeom,fit,fitBuffer,fitExtent,newAreaFeature
+};
+
+

+ 89 - 0
src/utils/turf_util.js

@@ -0,0 +1,89 @@
+import * as turf from '@turf/turf';
+import WKT from 'ol/format/WKT';
+import GeoJSON from 'ol/format/GeoJSON';
+const wktFormat = new WKT();
+const geoJSONFormat = new GeoJSON();
+
+
+class GeoUtils {
+    // 将 WKT 转换为经纬度数组
+    static wktToCoordinates(wktString) {
+        const geometry = wktFormat.readGeometry(wktString);
+        const coordinates = geometry.getCoordinates();
+        return coordinates
+    }
+
+    static mercatorToWGS84(x, y) {
+        const longitude = (x / 20037508.34) * 180;
+        const latitude = (Math.atan(Math.exp(y / 20037508.34 * Math.PI)) * 360 / Math.PI) - 90;
+        return {
+            lng: longitude,
+            lat: latitude
+        };
+    }
+
+    // 将 WKT 转换为 GeoJSON
+    static wktToGeoJSON(wktString) {
+        try {
+            const feature = wktFormat.readFeature(wktString);
+            return JSON.parse(geoJSONFormat.writeFeature(feature));
+        } catch (error) {
+            throw new Error('Invalid WKT format: ' + error.message);
+        }
+    }
+
+    static wktPointToCoordinates(wktString) {
+        try {
+            const feature = wktFormat.readFeature(wktString);
+            // 获取坐标
+            const coordinates = feature.getGeometry().getCoordinates();
+            // 返回经纬度数组
+            return coordinates;
+        } catch (error) {
+            throw new Error('Invalid WKT format: ' + error.message);
+        }
+    }
+
+    // 计算两点之间的距离
+    static calculateDistance(point1Wkt, point2Wkt) {
+        const point1 = this.wktPointToCoordinates(point1Wkt);
+        const point2 = this.wktPointToCoordinates(point2Wkt);
+        return turf.distance(turf.point(point1), turf.point(point2));
+    }
+
+    // 判断两条线段是否相交
+    static linesIntersect(line1Wkt, line2Wkt) {
+        const line1 = this.wktToGeoJSON(line1Wkt);
+        const line2 = this.wktToGeoJSON(line2Wkt);
+        return turf.booleanCrosses(line1, line2);
+    }
+
+    // 计算多边形面积
+    static calculateArea(polygonWkt) {
+        const polygon = this.wktToGeoJSON(polygonWkt);
+        return turf.area(polygon);
+    }
+
+    // 判断点是否在多边形内部
+    static isPointInPolygon(pointWkt, polygonWkt) {
+        const point = this.wktToGeoJSON(pointWkt);
+        const polygon = this.wktToGeoJSON(polygonWkt);
+        return turf.booleanPointInPolygon(point, polygon);
+    }
+}
+
+export default GeoUtils;
+
+// 使用示例
+// const point1Wkt = 'POINT(1 1)';
+// const point2Wkt = 'POINT(2 2)';
+// console.log('Distance:', GeoUtils.calculateDistance(point1Wkt, point2Wkt));
+//
+// const line1Wkt = 'LINESTRING(0 0, 2 2)';
+// const line2Wkt = 'LINESTRING(0 2, 2 0)';
+// console.log('Lines Intersect:', GeoUtils.linesIntersect(line1Wkt, line2Wkt));
+//
+// const polygonWkt = 'POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))';
+// console.log('Area:', GeoUtils.calculateArea(polygonWkt));
+//
+// console.log('Is Point in Polygon:', GeoUtils.isPointInPolygon(point1Wkt, polygonWkt));

+ 2 - 0
src/views/Index.vue

@@ -16,6 +16,7 @@ import ThreeFlavors from "./pages/ThreeFlavors.vue";
 import Certification from "./pages/Certification.vue"
 import TraceSource from "./pages/TraceSource.vue";
 import DefendLeague from "./pages/DefendLeague.vue"
+import ExponentialMap from "./pages/ExponentialMap.vue"
 const components = {  
   Home,  
   Exponent,
@@ -23,6 +24,7 @@ const components = {
   Certification,
   TraceSource,
   DefendLeague,
+  ExponentialMap
 };  
 
 const currentComponent = ref('Home');

+ 139 - 0
src/views/pages/ExponentialMap.vue

@@ -0,0 +1,139 @@
+<template>
+    <div class="exponential-map">
+        <!-- 地图 -->
+        <div ref="mapRef" class="map"></div>
+        <!-- 下拉框 -->
+        <el-select class="select" v-model="value" size="large" popper-class="common-dark-select-popper">
+            <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+    </div>
+</template>
+
+<script setup>
+import * as KMap from "@/utils/ol-map/KMap";
+import * as util from "@/utils/ol_common.js";
+import { newPoint } from "@/utils/map";
+import ImageStatic from "ol/source/ImageStatic";
+import ImageLayer from "ol/layer/Image";
+import VectorLayer from "ol/layer/Vector";
+import VectorSource from "ol/source/Vector";
+import GeoJSON from "ol/format/GeoJSON";
+import Style from "ol/style/Style";
+import { onMounted, ref } from "vue";
+import { map, featchShopList } from "@/api/modules/home";
+import MockFarmLayer from "./map/mockFarmLayer";
+
+const value = ref("Option1");
+
+const options = [
+    {
+        value: "Option1",
+        label: "荔枝",
+    },
+    {
+        value: "Option2",
+        label: "Option2",
+    },
+    {
+        value: "Option3",
+        label: "Option3",
+    },
+    {
+        value: "Option4",
+        label: "Option4",
+    },
+    {
+        value: "Option5",
+        label: "Option5",
+    },
+];
+
+const mapRef = ref(null);
+let kmap = null;
+
+let mockFarmLayer = null;
+
+const initMap = async () => {
+    let level = 7.6;
+    let coordinate = util.wktCastGeom("POINT(113.482483428125 23.442793525)").getFirstCoordinate();
+    kmap = new KMap.Map(mapRef.value, level, coordinate[0], coordinate[1], null, 2, 22, "null");
+
+    const guangdongLayer = loadCountyBoundary(); // 广东
+    kmap.addLayer(guangdongLayer);
+    getMockFarmLayer(kmap);
+};
+
+const getMockFarmLayer = (kmap) => {
+    mockFarmLayer = new MockFarmLayer(kmap);
+    featchShopList().then(({ data }) => {
+        mockFarmLayer.setData(data);
+    });
+};
+
+const loadCountyBoundary = () => {
+    let source = new VectorSource();
+    const cityLayer = new VectorLayer({
+        source,
+        zIndex: 999,
+    });
+    let vectorStyle = new KMap.VectorStyle();
+    let style1 = vectorStyle.getPolygonStyle("rgba(38, 136, 217, 0.13)", "rgba(0, 142, 255, 0.3)", 1);
+    let style2 = vectorStyle.getPolygonStyle("rgba(33, 153, 248, 0.5)", '"#0083EA', 2);
+    map().then(({ code, data }) => {
+        if (code === 0) {
+            let arr = [];
+            for (let i = 0; i < data.length; i++) {
+                let feature = util.newAreaFeature(data[i], "geom");
+                feature.setStyle(
+                    data[i].lighten ||
+                        data[i]["countyCode"] === "156440303" ||
+                        data[i]["countyCode"] === "156440306" ||
+                        data[i]["countyCode"] === "156440305"
+                        ? style2
+                        : style1
+                );
+                arr.push(feature);
+            }
+            source.addFeatures(arr);
+        }
+    });
+    return cityLayer;
+};
+
+onMounted(() => {
+    initMap();
+});
+</script>
+
+<style lang="scss" scoped>
+.exponential-map {
+    width: 100%;
+    height: 100vh;
+    position: relative;
+    .map {
+        width: 100%;
+        height: 100%;
+    }
+    .select{
+        position: absolute;
+        left: 24px;
+        top: 94px;
+        width: 180px;
+        ::v-deep{
+            .el-select__wrapper{
+                box-shadow: none;
+                background: rgba(0, 0, 0, 0.4);
+                border-radius: 12px 2px 12px 2px;
+                border: 1px solid #fff;
+                text-align: center;
+                padding: 13px;
+            }
+            .el-select__placeholder,.el-select__caret{
+                color: #FFDA75;
+                font-size: 20px;
+                font-family: "PangMenZhengDao";
+            }
+        }
+    }
+}
+</style>

+ 403 - 0
src/views/pages/map/mockFarmLayer.js

@@ -0,0 +1,403 @@
+import Style from "ol/style/Style";
+import Photo from "ol-ext/style/Photo";
+import { newPoint } from "@/utils/map";
+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 { extractCoordinates } from "@/common/commonFun"
+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 VectorLayer from "ol/layer/Vector.js";
+import router from '@/router'
+import { ElMessage } from "element-plus";
+
+/**
+ * 行政区县级天气点位数据
+ */
+class MockFarmLayer {
+    constructor(kmap) {
+        let that = this
+        this.nameStyleCache = {}
+        this.cloudFilenameCache = {}
+        this.statusTitleStyleCache = {}
+        this.textBgStyleCache = {}
+        this.bgStyleCache = {}
+        this.typeInfoStyleCache = {}
+        that.initLayer(kmap)
+        this.kmap = 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)
+        //     }
+        // })
+        // 地图点击事件
+        this.addMapSingerClick()
+    }
+
+
+    /*************************************************************************************************
+     图层初始化的函数
+     *************************************************************************************************/
+    initLayer(kmap) {
+        let that = this;
+        this.vectorStyle = new KMap.VectorStyle()
+        this.clusterSource = new VectorSource({})
+        this.clusterLayer = new Vector({
+            source: new Cluster({
+                distance: 30,
+                source: this.clusterSource,
+            }),
+            name: "MockFarmLayer",
+            minZoom: 6.8,
+            maxZoom: 22,
+            zIndex: 1001,
+            style: function (feature) {
+                let f = that.selectFeature(feature)
+                const isCurrent = f.get("selected");
+                const isHigh = f.get("isHigh") || f.get("recommend") == "1" || f.get("isDemo");
+                let imgType = f.get("pz") ? f.get("pz").slice(0, 2) : "荔枝";
+                let img = f.get("icon") || `https://birdseye-img.sysuimars.com/temp/pz/${imgType}.png`;
+                let farmId = f.get("farmId");
+                let styles = [that.cloudStyle(img, isCurrent, isHigh, farmId), that.pointBgStyle(farmId, f.get("name"), isCurrent, isHigh)];
+                that.setProperties(feature, f)
+                let text = f.get("pz") ? that.firstVariety(f.get("pz")) : "荔枝";
+                let fontColor = (isCurrent || isHigh) ? "#2199F8" : "#fff";
+                const startColor = (isCurrent || isHigh) ? "#57E2E2" : "#00000066"
+                const endColor = (isCurrent || isHigh) ? "#2199F8" : "#00000066"
+
+                const textOffsetY = farmId ? 15 : - 60
+
+                // styles.push(that.textBgStyle(startColor, endColor, isHigh));
+                styles.push(that.statusTitleStyle(f.get("name"), 0, textOffsetY, '#fff', "#000", (isCurrent || isHigh) ? 20 : 16));
+
+                return styles;
+            }
+        });
+        kmap.addLayer(this.clusterLayer);
+
+    }
+
+    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.point) {
+                item.wkt = item.point
+                try {
+                    features.push(newPoint(item, "wkt", "mock_farm_data"))
+                } catch (err) {
+                    console.log('err', err);
+                }
+            }
+        }
+        this.clusterSource.addFeatures(features)
+
+        // 查找荔博园
+        // const findItem = features.find(item => item.get("name") == "荔枝博览园")
+        // findItem.set("selected", true)
+        // findItem.set("isHight", true)
+    }
+
+    fitByGardenId(farmId) {
+        this.clusterSource.forEachFeature((f) => {
+            if (f.get("farmId") == farmId) {
+                // const extent = f.getGeometry().getExtent()
+                // this.kmap.getView().fit(extent, { padding: [160, 60, 340, 60], duration: 1500 });
+                // const currentZoom = this.kmap.getView().getZoom();
+                // if (currentZoom > 16) {
+                // // this.kmap.getView().setZoom(16);
+                // }
+                this.kmap.getView().animate({
+                    center: extractCoordinates(f.get("point")),
+                    zoom: 16,
+                    duration: 1500 // 动画持续时间,单位为毫秒
+                });
+            }
+        })
+    }
+    // 地图点击事件
+    addMapSingerClick() {
+        let that = this;
+        that.kmap.on("singleclick", (evt) => {
+            that.kmap.map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) {
+                if (layer instanceof VectorLayer && layer.get("name") === "MockFarmLayer") {
+                    let f = that.selectFeature(feature)
+                    const farmId = f.get("farmId")
+                    if (farmId) {
+                        sessionStorage.setItem("sessionFarmId", farmId)
+                        router.push(`/lj_home?farmId=${farmId}`)
+                    } else {
+                        ElMessage.warning('待开放')
+                    }
+                    // fs.length > 0 && eventBus.emit('MockFarmLayer:click', { event: evt, feature: fs[0] });
+                    return
+                }
+            })
+        })
+    }
+
+    /*************************************************************************************************
+        样式相关的函数
+     *************************************************************************************************/
+    /**
+     *
+     * @param startColor '#ffc91a'
+     * @param endColor '#d2a106'
+     * @returns {Style}
+     */
+    textBgStyle(startColor, endColor, isHigh) {
+        let key = startColor + endColor + isHigh
+        let style = this.textBgStyleCache[key]
+        if (!style) {
+            style = new Style({
+                renderer: function (coordinates, state) {
+                    let ctx = state.context;
+                    // 矩形的参数
+                    const x = coordinates[0]; // 矩形中心点的x坐标
+                    const y = coordinates[1] - (isHigh ? (88 * state.pixelRatio) : (70 * state.pixelRatio)); // 矩形中心点的y坐标
+                    const width = 76 * state.pixelRatio; // 矩形的宽度
+                    const height = 26 * state.pixelRatio; // 矩形的高度
+                    const cornerRadius = 12 * state.pixelRatio; // 圆角半径
+                    // 创建渐变
+                    const gradient = ctx.createLinearGradient(x - width / 2, y, x + width / 2, y);
+                    gradient.addColorStop(0, startColor);   // 渐变起始颜色
+                    gradient.addColorStop(1, endColor);  // 渐变结束颜色
+                    // 绘制圆角矩形
+                    ctx.beginPath();
+                    ctx.moveTo(x - width / 2 + cornerRadius, y - height / 2); // 左上角
+                    ctx.lineTo(x + width / 2 - cornerRadius, y - height / 2); // 上边
+                    ctx.arc(x + width / 2 - cornerRadius, y - height / 2 + cornerRadius, cornerRadius, -Math.PI / 2, 0); // 右上角
+                    ctx.lineTo(x + width / 2, y + height / 2 - cornerRadius); // 右边
+                    ctx.arc(x + width / 2 - cornerRadius, y + height / 2 - cornerRadius, cornerRadius, 0, Math.PI / 2); // 右下角
+                    ctx.lineTo(x - width / 2 + cornerRadius, y + height / 2); // 下边
+                    ctx.arc(x - width / 2 + cornerRadius, y + height / 2 - cornerRadius, cornerRadius, Math.PI / 2, Math.PI); // 左下角
+                    ctx.lineTo(x - width / 2, y - height / 2 + cornerRadius); // 左边
+                    ctx.arc(x - width / 2 + cornerRadius, y - height / 2 + cornerRadius, cornerRadius, Math.PI, -Math.PI / 2); // 左上角
+                    ctx.closePath();
+                    ctx.fillStyle = gradient;                // 填充颜色
+                    ctx.fill();
+                },
+                zIndex: 12
+            })
+            this.textBgStyleCache[key] = style
+        }
+        return style
+    }
+
+
+    textBgStyle2(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 = 8 * state.pixelRatio; // 左右内边距
+                    const dynamicWidth = textWidth + padding * 2; // 总宽度 = 文字宽度 + 内边距
+
+                    // 3. 矩形参数(高度和圆角不变)
+                    const x = coordinates[0];
+                    const y = (isCurrent || isHigh) ? (coordinates[1] - 110 * state.pixelRatio) : (coordinates[1] - 90 * state.pixelRatio);
+                    const height = 30 * state.pixelRatio;
+                    const cornerRadius = 15 * 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]
+        let that = this
+        if (!style) {
+            style = new Style({
+                text: new Text({
+                    text: statusName,
+                    offsetX: offsetX,
+                    offsetY: offsetY,
+                    // font: `${fontSize}px`,
+                    font: fontSize === 20 ? `bold ${fontSize}px sans-serif` : `${fontSize}px sans-serif`,
+                    fill: new Fill({ color}), // 字体颜色
+                    stroke: new Stroke({ color: strokeColor,width: fontSize === 20 ?1.5:1}), // 字体颜色
+                }),
+                zIndex: 13
+            });
+            this.statusTitleStyleCache[key] = style
+        }
+        return style
+    }
+    createGradient(colors, width, height) {
+        const canvas = document.createElement('canvas');
+        canvas.width = width;
+        canvas.height = height;
+        const ctx = canvas.getContext('2d');
+        const gradient = ctx.createLinearGradient(0, 0, 0, height);
+        colors.forEach((color, i) => {
+            gradient.addColorStop(i / (colors.length - 1), color);
+        });
+        ctx.fillStyle = gradient;
+        ctx.fillRect(0, 0, width, height);
+        return ctx.createPattern(canvas, 'repeat');
+    }
+
+    /**
+     * 农场照片样式
+     * @param cloudFilename
+     * @returns {Style}
+     */
+    cloudStyle(cloudFilename, isCurrent, isHigh, farmId) {
+        let that = this
+        const key = cloudFilename + isCurrent + isHigh + farmId
+        let cloudStyle = this.cloudFilenameCache[key]
+        if (!cloudStyle) {
+            let cloudUrl = `${cloudFilename}?imageView2/1/w/100`
+            cloudStyle = new Style({
+                image: new Photo({
+                    src: cloudUrl,
+                    radius:  (isHigh ? 29 : 21.5),
+                    crop: true,
+                    displacement: [0,  (isCurrent || isHigh ? 43 : 36.6)],
+                    stroke: new Stroke({
+                        width: 0,
+                        color: "#fdfcfc00",
+                    }),
+                    fill: new Fill({
+                        color: 'rgba(255, 255, 255)',
+                    }),
+                    onload: function () {
+                        that.clusterLayer &&
+                            that.clusterLayer.changed();
+                    },
+                }),
+                zIndex:  (isHigh ? 22 : 12)
+            })
+            this.cloudFilenameCache[key] = cloudStyle
+        }
+        return cloudStyle
+    }
+
+    /**
+     * 点位背景样式
+     * @returns {Style}
+     */
+    pointBgStyle(farmId, name, isCurrent, isHigh) {
+        const key = farmId + name + isCurrent + isHigh
+        let bgStyle = this.bgStyleCache[key]
+        if (!bgStyle) {
+            bgStyle = new Style({
+                image: new Photo({
+                    src: ((isHigh || isCurrent) ? require("@/assets/images/map/point-bg-active.png") : require("@/assets/images/map/point-bg.png")),
+                    radius: ((isHigh || isCurrent) ? 50 : 29.5),
+                    shadow: 0,
+                    crop: false,
+                    displacement: [(isHigh || isCurrent)?18:0, ((isHigh || isCurrent) ? 38 : 31)],
+                    stroke: new Stroke({
+                        width: 0,
+                        color: "#fdfcfc00",
+                    }),
+                    fill: new Fill({
+                        color: '#fff',
+                    }),
+                }),
+                zIndex: (isHigh ? 32 : 22),
+            });
+            this.bgStyleCache[key] = bgStyle
+        }
+        return bgStyle
+    }
+
+    /*************************************************************************************************
+     其他函数
+     *************************************************************************************************/
+    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("recommend") == "1" || item.get("isDemo")) {
+                    return item;
+                }
+            }
+            // 如果都没有,返回第一个项
+            return fs[0];
+        }
+    }
+
+}
+
+export default MockFarmLayer;
+// new MockFarmLayer()

Some files were not shown because too many files changed in this diff