drawRegionMap.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. import * as KMap from "@/utils/ol-map/KMap";
  2. import * as util from "@/common/ol_common.js";
  3. import config from "@/api/config.js";
  4. import Style from "ol/style/Style";
  5. import Icon from "ol/style/Icon";
  6. import { Fill, Stroke, Text } from "ol/style.js";
  7. import { Point } from 'ol/geom';
  8. import Feature from "ol/Feature";
  9. import * as proj from "ol/proj";
  10. import { getArea } from 'ol/sphere.js';
  11. import WKT from "ol/format/WKT.js";
  12. import proj4 from "proj4"
  13. import { register } from "ol/proj/proj4";
  14. proj4.defs("EPSG:38572", "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs");
  15. register(proj4);
  16. /**
  17. * @description 地图层对象
  18. */
  19. class DrawRegionMap {
  20. constructor() {
  21. let that = this;
  22. let vectorStyle = new KMap.VectorStyle();
  23. this.vectorStyle = vectorStyle;
  24. // 是否允许编辑(勾画页 true,小弹窗回显页 false)
  25. this.editable = true;
  26. // 位置图标
  27. this.clickPointLayer = new KMap.VectorLayer("clickPointLayer", 9999, {
  28. style: () => {
  29. return new Style({
  30. image: new Icon({
  31. // src: require("@/assets/img/home/garden-point.png"),
  32. src: require("@/assets/img/map/map_point.png"),
  33. scale: 0.5,
  34. // anchor: [0.5, 0.5],
  35. }),
  36. });
  37. },
  38. });
  39. // 只读状态区域图层(展示多 polygon,用于“已解决 / 未解决”等状态)
  40. // 层级需低于默认可编辑 polygonLayer(1000),避免标签遮挡当前编辑地块
  41. this.staticRegionLayer = new KMap.VectorLayer("staticRegionLayer", 900, {
  42. style: (f) => {
  43. const displayMode = f.get("displayMode");
  44. // 品种勾画页:已勾画的其他品种仅做只读灰色展示,并显示品种名
  45. if (displayMode === "readonlyVariety") {
  46. // 品种区只读展示:统一使用品种区视觉样式
  47. return new Style({
  48. fill: new Fill({ color: "rgba(0, 57, 44, 0.5)" }),
  49. stroke: new Stroke({ color: "#18AA8B", width: 1.5 }),
  50. text: new Text({
  51. text: f.get("label") || "",
  52. font: "12px sans-serif",
  53. fill: new Fill({ color: "#ffffff" }),
  54. backgroundFill: new Fill({ color: "rgba(0, 57, 44, 0.85)" }),
  55. padding: [2, 6, 2, 6],
  56. }),
  57. });
  58. }
  59. if (displayMode === "lockedDisease") {
  60. const status = String(f.get("handleStatus") || "");
  61. const lockStyleType = String(f.get("lockStyleType") || "");
  62. const isControlled = lockStyleType === "controlled" || status === "3";
  63. const isGrowthTreating = lockStyleType === "growthTreating";
  64. const label = f.get("label") || (isControlled ? "病害已控制" : "病害治疗中");
  65. const updatedTime = f.get("updatedTime") || "";
  66. const isNewAbnormal = String(label).startsWith("新增");
  67. let areaText = "";
  68. try {
  69. let geom = f.getGeometry().clone();
  70. geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"));
  71. let area = getArea(geom);
  72. area = (area + area / 2) / 1000;
  73. areaText = `${Number(area).toFixed(2)}亩`;
  74. } catch (_) {
  75. areaText = "";
  76. }
  77. const fillColor = isControlled
  78. ? "rgba(166, 166, 166, 0.25)"
  79. : isGrowthTreating
  80. ? "rgba(255, 159, 102, 0.3)"
  81. : "rgba(191, 91, 91, 0.36)";
  82. const strokeColor = isControlled
  83. ? "#A6A6A6"
  84. : isGrowthTreating
  85. ? "rgba(255, 94, 0, 0.3)"
  86. : "rgba(224, 49, 49, 0.3)";
  87. const badgeTextColor = isControlled
  88. ? "#ffffff"
  89. : isNewAbnormal
  90. ? "#ffffff"
  91. : isGrowthTreating
  92. ? "#F76F00"
  93. : "#E32A28";
  94. const badgeBgColor = isControlled
  95. ? "#A6A6A6"
  96. : isNewAbnormal
  97. ? (isGrowthTreating ? "#F76F00" : "#E32A28")
  98. : "#ffffff";
  99. const badgeBorderColor = isControlled || isNewAbnormal ? "" : isGrowthTreating ? "#F76F00" : "#E32A28";
  100. const styles = [
  101. new Style({
  102. fill: new Fill({ color: fillColor }),
  103. stroke: new Stroke({ color: strokeColor, width: 1.5 }),
  104. text: new Text({
  105. text: label,
  106. font: "bold 13px sans-serif",
  107. fill: new Fill({ color: badgeTextColor }),
  108. backgroundFill: new Fill({ color: badgeBgColor }),
  109. backgroundStroke: badgeBorderColor
  110. ? new Stroke({ color: badgeBorderColor, width: 1 })
  111. : undefined,
  112. padding: [4, 10, 4, 10],
  113. offsetY: -40,
  114. }),
  115. }),
  116. ];
  117. if (updatedTime) {
  118. styles.push(
  119. new Style({
  120. text: new Text({
  121. text: `发现时间:${updatedTime}`,
  122. font: "12px sans-serif",
  123. fill: new Fill({ color: isNewAbnormal ? "#333333" : "#ffffff" }),
  124. ...(isNewAbnormal
  125. ? {
  126. backgroundFill: new Fill({ color: "#ffffff" }),
  127. padding: [8, 12, 8, 12],
  128. }
  129. : {}),
  130. offsetY: -16,
  131. }),
  132. })
  133. );
  134. }
  135. if (areaText) {
  136. styles.push(
  137. new Style({
  138. text: new Text({
  139. text: areaText,
  140. font: "16px sans-serif",
  141. fill: new Fill({ color: "#ffffff" }),
  142. offsetY: 14,
  143. }),
  144. })
  145. );
  146. }
  147. return styles;
  148. }
  149. const status = f.get("status"); // 'resolved' | 'unresolved'
  150. const reproductiveName = f.get("reproductiveName") || "";
  151. const isResolved = status === "resolved";
  152. const unresolvedBlueFill = f.get("unresolvedBlueFill") === true;
  153. const regionStyleKind = String(f.get("regionStyleKind") || "");
  154. const isAbnormalGrowth =
  155. regionStyleKind === "ABNORMAL" &&
  156. (reproductiveName.includes("长势") ||
  157. reproductiveName.includes("过慢") ||
  158. reproductiveName.includes("过快"));
  159. let unresolvedFillColor = unresolvedBlueFill
  160. ? "rgba(33, 153, 248, 0.35)"
  161. : "rgba(0, 0, 0, 0.5)";
  162. let unresolvedStrokeColor = "#2199F8";
  163. let unresolvedBadgeColor = "#2199F8";
  164. if (regionStyleKind === "variety") {
  165. unresolvedFillColor = "rgba(0, 57, 44, 0.5)";
  166. unresolvedStrokeColor = "#18AA8B";
  167. unresolvedBadgeColor = "#18AA8B";
  168. } else if (regionStyleKind === "ABNORMAL") {
  169. unresolvedFillColor = isAbnormalGrowth
  170. ? "rgba(124, 46, 0, 0.5)"
  171. : "rgba(100, 0, 0, 0.5)";
  172. unresolvedStrokeColor = isAbnormalGrowth ? "#FF7300" : "#E03131";
  173. unresolvedBadgeColor = isAbnormalGrowth ? "#F76F00" : "#E03131";
  174. } else if (regionStyleKind === "ENVIRONMENT") {
  175. unresolvedFillColor = "rgba(151, 96, 0, 0.5)";
  176. unresolvedStrokeColor = "#FDCF7F";
  177. unresolvedBadgeColor = "#FDCF7F";
  178. }
  179. // 已解决:深灰填充,浅白描边;未解决:默认深灰半透明 + 蓝描边;unresolvedBlueFill 时浅蓝填充(互动列表等)
  180. const fillColor = isResolved
  181. ? "rgba(0, 0, 0, 0.6)"
  182. : unresolvedFillColor;
  183. const strokeColor = isResolved ? "#7C7C7C" : unresolvedStrokeColor;
  184. let abnormalBadgeText = "";
  185. if (regionStyleKind === "ABNORMAL") {
  186. if (reproductiveName.includes("病害")) {
  187. abnormalBadgeText = "新增病害";
  188. } else if (reproductiveName.includes("虫害")) {
  189. abnormalBadgeText = "新增虫害";
  190. } else if (reproductiveName.includes("过慢")) {
  191. abnormalBadgeText = "新增长势过慢";
  192. } else if (reproductiveName.includes("过快")) {
  193. abnormalBadgeText = "新增长势过快";
  194. }
  195. }
  196. const statusText = status === "resolved" ? "已解决" : "未解决";
  197. const mainLabelText =
  198. regionStyleKind === "ABNORMAL" && abnormalBadgeText
  199. ? abnormalBadgeText
  200. : reproductiveName
  201. ? `${reproductiveName} ${statusText}`
  202. : statusText;
  203. let areaText = "";
  204. try {
  205. let geom = f.getGeometry().clone();
  206. geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"));
  207. let area = getArea(geom);
  208. area = (area + area / 2) / 1000;
  209. areaText = `${Number(area).toFixed(2)}亩`;
  210. } catch (_) {
  211. areaText = "";
  212. }
  213. const text = new Text({
  214. text: mainLabelText,
  215. font: regionStyleKind === "ABNORMAL" ? "bold 13px sans-serif" : "12px sans-serif",
  216. fill: new Fill({ color: "#ffffff" }),
  217. backgroundFill: new Fill({
  218. color: isResolved ? "#949494" : unresolvedBadgeColor
  219. }),
  220. padding: [1, 5, 1, 5],
  221. offsetY: -40,
  222. });
  223. const style = new Style({
  224. fill: new Fill({ color: fillColor }),
  225. stroke: new Stroke({ color: strokeColor, width: 1 }),
  226. text: text,
  227. })
  228. const text2 = new Style({
  229. text: new Text({
  230. text: `发现时间:${f.get("updatedTime")}`,
  231. font: "12px sans-serif",
  232. offsetY: -16,
  233. fill: new Fill({ color: "#ffffff" }),
  234. ...(regionStyleKind === "ABNORMAL"
  235. ? {}
  236. : {
  237. backgroundFill: new Fill({
  238. color: isResolved ? "rgba(171, 171, 171, 0.4)" : `${unresolvedBadgeColor}99`,
  239. }),
  240. }),
  241. padding: [1, 5, 1, 5],
  242. }),
  243. });
  244. const styles = [style, text2];
  245. if (areaText) {
  246. styles.push(
  247. new Style({
  248. text: new Text({
  249. text: areaText,
  250. font: "16px sans-serif",
  251. fill: new Fill({ color: "#ffffff" }),
  252. offsetY: 14,
  253. }),
  254. })
  255. );
  256. }
  257. return styles;
  258. },
  259. });
  260. }
  261. /**
  262. * 初始化地图
  263. * @param {string} location WKT 点位
  264. * @param {HTMLElement|string} target 地图容器
  265. * @param {boolean} editable 是否允许绘制/编辑地块
  266. * @param {boolean} movable 是否允许拖动/缩放地图
  267. * @param {boolean} showPoint 是否显示初始点位图标
  268. * @param {Function} onDrawEnd 绘制闭环回调(drawend),可选
  269. */
  270. initMap(location, target, editable = true, movable = true, showPoint = true, onDrawEnd) {
  271. let level = 16;
  272. let coordinate = util.wktCastGeom(location).getFirstCoordinate();
  273. this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 8, 22);
  274. // 记录当前地图是否可编辑,供样式控制使用
  275. this.editable = editable;
  276. let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
  277. this.kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2);
  278. this.kmap.addLayer(this.clickPointLayer.layer);
  279. this.kmap.addLayer(this.staticRegionLayer.layer);
  280. // 根据 showPoint 决定是否在初始化时落下点位图标
  281. if (showPoint) {
  282. this.setMapPoint(coordinate);
  283. } else {
  284. this.clickPointLayer.source.clear();
  285. }
  286. // 仅在 editable 为 true 时开启绘制/编辑能力(用于勾画页面)
  287. if (editable) {
  288. this.kmap.initDraw((e) => {
  289. if (typeof onDrawEnd === "function") {
  290. try {
  291. onDrawEnd(e);
  292. } catch (_) {
  293. // 回调失败不影响地图继续使用
  294. }
  295. }
  296. });
  297. this.kmap.startDraw()
  298. this.kmap.modifyDraw()
  299. }
  300. // movable 为 false 时,禁用地图拖动、缩放等交互(用于小弹窗只看不动)
  301. if (!movable && this.kmap && this.kmap.setStates) {
  302. this.kmap.setStates({
  303. DoubleClickZoom: false,
  304. DragAndDrop: false,
  305. MouseWheelZoom: false,
  306. });
  307. }
  308. }
  309. /**
  310. * 回显地块
  311. * @param {string[]} geometryArr 多边形 WKT 数组
  312. * @param {boolean} needFitView 是否自动缩放视图
  313. * @param {string|number} areaText 显示的面积(单位:亩),可选
  314. * @param {{ fill?: string, stroke?: string }} [readonlyAreaStyle] 只读模式下覆盖填充/描边色;不传则仍用 Map.drawStyleColors 或默认
  315. * @param {{ badgeText: string, discoveryDate: string, badgeBackground?: string }|function} [growthOverlay] 只读模式下异常区标签(可传函数按地块动态返回)
  316. * @param {string} [polygonCenterLabel] 只读模式下覆盖多边形中心文案(原样展示,不加「亩」);用于品种查看态显示品种名等
  317. */
  318. setAreaGeometry(geometryArr, needFitView = false, areaText, readonlyAreaStyle, growthOverlay, polygonCenterLabel) {
  319. // 兜底保护:geometryArr 可能为 undefined/null 或空数组
  320. if (!Array.isArray(geometryArr) || geometryArr.length === 0) return;
  321. // 地图实例或图层尚未初始化时也直接返回,避免报错
  322. if (!this.kmap || !this.kmap.polygonLayer || !this.kmap.polygonLayer.source) return;
  323. let that = this;
  324. geometryArr.map((item, index) => {
  325. // 不使用 setLayerWkt,而是手动添加要素,避免自动缩放视图
  326. const format = new WKT()
  327. const mapProjection = that.kmap.map.getView().getProjection()
  328. let geometry = format.readGeometry(item, {
  329. dataProjection: 'EPSG:4326',
  330. featureProjection: mapProjection
  331. })
  332. let f = new Feature({ geometry: geometry })
  333. // 只读模式下,为多边形单独设置样式:仅填充+边框 + 面积文本,不显示可拖动的顶点小圆点
  334. if (!this.editable) {
  335. const resolvedGrowthOverlay =
  336. typeof growthOverlay === "function"
  337. ? growthOverlay(f, item, index)
  338. : growthOverlay;
  339. const overlayStatus = String(resolvedGrowthOverlay?.handleStatus || "");
  340. const overlayBadgeText = String(resolvedGrowthOverlay?.badgeText || "");
  341. const overlayLockStyleType = String(resolvedGrowthOverlay?.lockStyleType || "");
  342. // 只读查看态颜色与编辑态保持一致:
  343. // 1) 已控制(3) 灰色;2) 长势治疗中(2) 橙色;3) 病虫害治疗中(2) 红色
  344. const isControlledDisease = overlayStatus === "3";
  345. const isTreatingGrowth =
  346. overlayStatus === "2" &&
  347. (overlayLockStyleType === "growthTreating" || overlayBadgeText.includes("长势"));
  348. const isTreatingDisease = overlayStatus === "2" && !isTreatingGrowth;
  349. // 查看模式下单块区域展示:优先 readonlyAreaStyle,其次 Map.drawStyleColors,再默认
  350. const fillColor = isControlledDisease
  351. ? "rgba(166, 166, 166, 0.25)"
  352. : isTreatingGrowth
  353. ? "rgba(255, 159, 102, 0.3)"
  354. : isTreatingDisease
  355. ? "rgba(191, 91, 91, 0.36)"
  356. : (readonlyAreaStyle?.fill ??
  357. KMap.Map?.drawStyleColors?.fill ??
  358. "rgba(0, 57, 44, 0.5)");
  359. const strokeColor = isControlledDisease
  360. ? "#A6A6A6"
  361. : isTreatingGrowth
  362. ? "rgba(255, 94, 0, 0.3)"
  363. : isTreatingDisease
  364. ? "rgba(224, 49, 49, 0.3)"
  365. : (readonlyAreaStyle?.stroke ??
  366. KMap.Map?.drawStyleColors?.stroke ??
  367. "#18AA8B");
  368. const styles = [
  369. new Style({
  370. fill: new Fill({
  371. color: fillColor,
  372. }),
  373. stroke: new Stroke({
  374. color: strokeColor,
  375. width: 2,
  376. }),
  377. }),
  378. ];
  379. // 中心文本:优先 polygonCenterLabel(品种查看态显示名称);否则按面积规则
  380. const trimmedCenterLabel =
  381. typeof polygonCenterLabel === "string" && polygonCenterLabel.trim() !== ""
  382. ? polygonCenterLabel.trim()
  383. : "";
  384. // 面积文本显示规则:
  385. // 1) 传空字符串:不显示
  386. // 2) 传了值:优先显示传入值
  387. // 3) 未传:按当前地块实时计算亩数
  388. const isExplicitEmptyText = typeof areaText === "string" && areaText.trim() === "";
  389. const hasProvidedAreaText = areaText !== undefined && areaText !== null;
  390. let textValue = "";
  391. if (trimmedCenterLabel) {
  392. textValue = trimmedCenterLabel;
  393. } else if (!isExplicitEmptyText) {
  394. if (hasProvidedAreaText) {
  395. textValue = `${areaText}亩`;
  396. } else {
  397. try {
  398. let geom = geometry.clone();
  399. geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"));
  400. let areaItem = getArea(geom);
  401. areaItem = (areaItem + areaItem / 2) / 1000;
  402. textValue = `${Number(areaItem).toFixed(2)}亩`;
  403. } catch (_) {
  404. textValue = "";
  405. }
  406. }
  407. }
  408. const hasGrowth =
  409. resolvedGrowthOverlay &&
  410. resolvedGrowthOverlay.badgeText &&
  411. resolvedGrowthOverlay.discoveryDate;
  412. if (textValue) {
  413. // 品种名与 staticRegionLayer.readonlyVariety 标签视觉一致(12px + 深绿底)
  414. const isVarietyNameLabel = !!trimmedCenterLabel;
  415. styles.push(
  416. new Style({
  417. text: new Text({
  418. text: textValue,
  419. font: isVarietyNameLabel ? "12px sans-serif" : "15px sans-serif",
  420. fill: new Fill({ color: "#ffffff" }),
  421. ...(isVarietyNameLabel
  422. ? {
  423. backgroundFill: new Fill({
  424. color:
  425. KMap.Map?.drawStyleColors?.stroke
  426. ? `${KMap.Map.drawStyleColors.stroke}D9`
  427. : "rgba(0, 57, 44, 0.85)",
  428. }),
  429. padding: [2, 6, 2, 6],
  430. }
  431. : {}),
  432. offsetY: hasGrowth ? 14 : 0,
  433. }),
  434. })
  435. );
  436. }
  437. if (hasGrowth) {
  438. styles.push(
  439. new Style({
  440. text: new Text({
  441. text: resolvedGrowthOverlay.badgeText,
  442. font: "bold 13px sans-serif",
  443. fill: new Fill({ color: resolvedGrowthOverlay.badgeTextColor || "#ffffff" }),
  444. backgroundFill: new Fill({
  445. color: resolvedGrowthOverlay.badgeBackground || "#FF7F00",
  446. }),
  447. backgroundStroke: resolvedGrowthOverlay.badgeBorderColor
  448. ? new Stroke({ color: resolvedGrowthOverlay.badgeBorderColor, width: 1 })
  449. : undefined,
  450. padding: [4, 10, 4, 10],
  451. offsetY: -40,
  452. }),
  453. }),
  454. new Style({
  455. text: new Text({
  456. text: `发现时间:${resolvedGrowthOverlay.discoveryDate}`,
  457. font: "12px sans-serif",
  458. fill: new Fill({ color: "#ffffff" }),
  459. offsetY: -16,
  460. }),
  461. })
  462. );
  463. }
  464. f.setStyle(styles);
  465. }
  466. that.kmap.polygonLayer.source.addFeature(f)
  467. })
  468. // 根据参数决定是否需要自适应地块范围
  469. if (needFitView) {
  470. this.fitView()
  471. }
  472. }
  473. fitView() {
  474. if (!this.kmap?.polygonLayer?.source) return;
  475. const extent = this.kmap.polygonLayer.source.getExtent();
  476. if (
  477. !extent ||
  478. !isFinite(extent[0]) ||
  479. !isFinite(extent[1]) ||
  480. !isFinite(extent[2]) ||
  481. !isFinite(extent[3])
  482. ) {
  483. return;
  484. }
  485. this.kmap.getView().fit(extent, { duration: 500, padding: [10, 10, 10, 10] });
  486. }
  487. clearLayer() {
  488. // this.kmap.removeLayer(this.clickPointLayer.layer)
  489. if (this.kmap && this.kmap.polygonLayer) {
  490. this.kmap.polygonLayer.source.clear();
  491. }
  492. if (this.staticRegionLayer && this.staticRegionLayer.source) {
  493. this.staticRegionLayer.source.clear();
  494. }
  495. }
  496. /** 取消当前未完成的勾画(草图在 Draw 的 overlay 上,仅 clear 多边形图层去不掉) */
  497. abortOngoingDrawSketch() {
  498. if (!this.kmap || !this.kmap.draw || typeof this.kmap.draw.abortDrawing !== "function") return;
  499. this.kmap.draw.abortDrawing();
  500. }
  501. /**
  502. * 销毁地图实例(用于从编辑态切换到仅查看时重新初始化)
  503. */
  504. destroyMap() {
  505. this.clearLayer();
  506. if (this.kmap && typeof this.kmap.destroy === "function") {
  507. this.kmap.destroy();
  508. }
  509. this.kmap = null;
  510. }
  511. getAreaGeometry() {
  512. const features = this.kmap.getLayerFeatures()
  513. let geometryArr = []
  514. let area = 0
  515. const format = new WKT()
  516. // 获取图层上的Polygon,转成WKT用于回显
  517. features.forEach(item => {
  518. // 使用 writeGeometry 而不是 writeFeature,因为 setLayerWkt 期望的是几何体的 WKT
  519. const geometry = item.getGeometry()
  520. geometryArr.push(format.writeGeometry(geometry, {
  521. dataProjection: 'EPSG:4326',
  522. featureProjection: this.kmap.map.getView().getProjection()
  523. }))
  524. let geom = geometry.clone()
  525. geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"))
  526. let areaItem = getArea(geom)
  527. areaItem = (areaItem + areaItem / 2) / 1000;
  528. area += areaItem
  529. })
  530. return { geometryArr, mianji: area.toFixed(2) } // 修改为 mianji 字段,与创建页面保持一致
  531. }
  532. setMapPosition(center) {
  533. this.kmap.getView().animate({
  534. center,
  535. zoom: 17,
  536. duration: 500,
  537. });
  538. this.setMapPoint(center)
  539. }
  540. setMapPoint(coordinate) {
  541. this.clickPointLayer.source.clear()
  542. let point = new Feature(new Point(coordinate))
  543. this.clickPointLayer.addFeature(point)
  544. }
  545. // 删除当前地块(删除最新绘制的一个地块)
  546. deleteCurrentPolygon() {
  547. if (!this.kmap || !this.kmap.polygonLayer) return;
  548. const features = this.kmap.polygonLayer.source.getFeatures();
  549. if (features && features.length > 0) {
  550. const lastFeature = features[features.length - 1];
  551. this.kmap.polygonLayer.source.removeFeature(lastFeature);
  552. }
  553. }
  554. /**
  555. * 设置只读状态区域图层(多个 polygon,不可编辑)
  556. * @param {Array<{ geometry: string, status?: 'resolved' | 'unresolved', label?: string, displayMode?: string, unresolvedBlueFill?: boolean }>} regions
  557. *
  558. * 使用示例:
  559. * drawRegionMap.setStatusRegions([
  560. * { geometry: 'MULTIPOLYGON(((...)))', status: 'resolved' },
  561. * { geometry: 'MULTIPOLYGON(((...)))', status: 'unresolved' },
  562. * ]);
  563. */
  564. setStatusRegions(regions) {
  565. if (!this.kmap || !this.staticRegionLayer || !this.staticRegionLayer.source) return;
  566. // 仅操作只读图层,不影响当前地图的绘制 / 编辑状态
  567. this.staticRegionLayer.source.clear();
  568. if (!Array.isArray(regions) || regions.length === 0) return;
  569. const format = new WKT();
  570. const mapProjection = this.kmap.map.getView().getProjection();
  571. regions.forEach((region) => {
  572. if (!region || !region.geometry) return;
  573. try {
  574. const geometry = format.readGeometry(region.geometry, {
  575. dataProjection: "EPSG:4326",
  576. featureProjection: mapProjection,
  577. });
  578. const feature = new Feature({ geometry });
  579. feature.set("reproductiveName", region.reproductiveName);
  580. feature.set("status", region.status || "unresolved");
  581. feature.set("updatedTime", region.updatedTime);
  582. feature.set("label", region.label || "");
  583. feature.set("displayMode", region.displayMode || "");
  584. feature.set("handleStatus", region.handleStatus);
  585. feature.set("lockStyleType", region.lockStyleType || "");
  586. feature.set("unresolvedBlueFill", region.unresolvedBlueFill === true);
  587. feature.set("regionStyleKind", region.regionStyleKind || "");
  588. this.staticRegionLayer.addFeature(feature);
  589. } catch (e) {
  590. // 单个区域解析失败时忽略
  591. }
  592. });
  593. }
  594. /**
  595. * 视图自适应到「只读区域图层 + 可编辑多边形图层」的联合范围
  596. * 适用于同时存在接口返回区域和本地勾画区域时,保证都能出现在视野内
  597. */
  598. fitAllRegions() {
  599. if (!this.kmap) return;
  600. const extents = [];
  601. // 只读状态区域图层范围
  602. if (this.staticRegionLayer && this.staticRegionLayer.source) {
  603. const features = this.staticRegionLayer.source.getFeatures();
  604. if (features && features.length > 0) {
  605. extents.push(this.staticRegionLayer.source.getExtent());
  606. }
  607. }
  608. // 可编辑 polygon 图层范围
  609. if (this.kmap.polygonLayer && this.kmap.polygonLayer.source) {
  610. const features = this.kmap.polygonLayer.source.getFeatures();
  611. if (features && features.length > 0) {
  612. extents.push(this.kmap.polygonLayer.source.getExtent());
  613. }
  614. }
  615. if (extents.length === 0) return;
  616. // 计算所有范围的并集 [minX, minY, maxX, maxY]
  617. const merged = extents.reduce(
  618. (acc, cur) => {
  619. if (!acc) return cur.slice();
  620. return [
  621. Math.min(acc[0], cur[0]),
  622. Math.min(acc[1], cur[1]),
  623. Math.max(acc[2], cur[2]),
  624. Math.max(acc[3], cur[3]),
  625. ];
  626. },
  627. null
  628. );
  629. if (merged) {
  630. this.kmap.getView().fit(merged, { duration: 500, padding: [100, 100, 100, 100] });
  631. }
  632. }
  633. }
  634. export default DrawRegionMap;