growthReportMap.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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 StaticImgLayer from "@/utils/ol-map/StaticImgLayer";
  5. import Style from "ol/style/Style";
  6. import Icon from "ol/style/Icon";
  7. import Fill from "ol/style/Fill";
  8. import Stroke from "ol/style/Stroke";
  9. import { Point, Polygon } from "ol/geom";
  10. import Feature from "ol/Feature";
  11. import Overlay from "ol/Overlay";
  12. import WKT from "ol/format/WKT.js";
  13. import { reactive } from "vue";
  14. export let mapLocation = reactive({
  15. data: null,
  16. });
  17. /** 无范围时中心示意方框边长(米) */
  18. const NO_RANGE_SIDE_METERS = 280;
  19. /**
  20. * @description 地图层对象
  21. */
  22. class IndexMap {
  23. constructor() {
  24. let that = this;
  25. let vectorStyle = new KMap.VectorStyle();
  26. this.vectorStyle = vectorStyle;
  27. this.noRangeTipOverlay = null;
  28. this.remoteImageLayer = null;
  29. // 位置图标
  30. this.clickPointLayer = new KMap.VectorLayer("clickPointLayer", 9999, {
  31. style: (f) => {
  32. return new Style({
  33. image: new Icon({
  34. src: require("@/assets/img/home/garden-point.png"),
  35. scale: 0.5,
  36. }),
  37. });
  38. },
  39. });
  40. // 无勾画范围:中心点示意正方形
  41. this.noRangeGuideLayer = new KMap.VectorLayer("noRangeGuideLayer", 9998, {
  42. style: () =>
  43. new Style({
  44. fill: new Fill({ color: "rgba(0, 0, 0, 0.28)" }),
  45. stroke: new Stroke({
  46. color: "rgba(255, 255, 255, 0.95)",
  47. width: 1.5,
  48. lineDash: [6, 4],
  49. }),
  50. }),
  51. });
  52. }
  53. /** 叠加静态风险底图(与 demo growthReportMap.showRemoteImage 一致) */
  54. showRemoteImage(url, extent, options = {}) {
  55. this.hideRemoteImage();
  56. if (!url || !extent?.length || !this.kmap) return;
  57. this.remoteImageLayer = new StaticImgLayer(
  58. url,
  59. {
  60. extent,
  61. minZoom: 6,
  62. maxZoom: 22,
  63. opacity: options.opacity ?? 0.5,
  64. },
  65. 50,
  66. this.kmap,
  67. );
  68. }
  69. hideRemoteImage() {
  70. if (this.remoteImageLayer?.layer && this.kmap?.map) {
  71. this.kmap.map.removeLayer(this.remoteImageLayer.layer);
  72. }
  73. this.remoteImageLayer = null;
  74. }
  75. initMap(location, target) {
  76. let level = 16;
  77. let coordinate = util.wktCastGeom(location).getFirstCoordinate();
  78. this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 8, 22);
  79. let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
  80. this.kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2);
  81. this.kmap.addLayer(this.noRangeGuideLayer.layer);
  82. this.kmap.addLayer(this.clickPointLayer.layer);
  83. this.setMapPoint(coordinate)
  84. // this.addMapSingerClick()
  85. }
  86. setMapPosition(center) {
  87. this.kmap.getView().animate({
  88. center,
  89. zoom: 16,
  90. duration: 0,
  91. });
  92. this.setMapPoint(center)
  93. }
  94. setMapPoint(coordinate) {
  95. this.clickPointLayer.source.clear()
  96. let point = new Feature(new Point(coordinate))
  97. this.clickPointLayer.addFeature(point)
  98. mapLocation.data = coordinate
  99. // 示意框跟着中心点走
  100. if (this.noRangeGuideLayer?.source?.getFeatures?.()?.length) {
  101. this.drawNoRangeSquare(coordinate);
  102. this.updateNoRangeTipPosition(coordinate);
  103. }
  104. }
  105. /** 以中心点生成正方形环坐标(EPSG:4326) */
  106. buildSquareRing(center, sideLengthMeters = NO_RANGE_SIDE_METERS) {
  107. const lng = Number(center[0]);
  108. const lat = Number(center[1]);
  109. const half = sideLengthMeters / 2;
  110. const latDelta = half / 111000;
  111. const lngDelta = half / (111000 * Math.cos((lat * Math.PI) / 180));
  112. return [
  113. [lng - lngDelta, lat + latDelta],
  114. [lng + lngDelta, lat + latDelta],
  115. [lng + lngDelta, lat - latDelta],
  116. [lng - lngDelta, lat - latDelta],
  117. [lng - lngDelta, lat + latDelta],
  118. ];
  119. }
  120. drawNoRangeSquare(coordinate) {
  121. if (!this.noRangeGuideLayer?.source || !coordinate?.length) return;
  122. this.noRangeGuideLayer.source.clear();
  123. const ring = this.buildSquareRing(coordinate);
  124. const feature = new Feature(new Polygon([ring]));
  125. this.noRangeGuideLayer.addFeature(feature);
  126. }
  127. updateNoRangeTipPosition(coordinate) {
  128. if (!this.noRangeTipOverlay || !coordinate?.length) return;
  129. const ring = this.buildSquareRing(coordinate);
  130. // 方框底边中点略偏下,提示贴在方框下方
  131. const tipCoord = [(ring[2][0] + ring[3][0]) / 2, ring[2][1] - (ring[0][1] - ring[2][1]) * 0.08];
  132. this.noRangeTipOverlay.setPosition(tipCoord);
  133. }
  134. /** 无范围引导:中心示意方框 + 下方提示 Overlay */
  135. showNoRangeGuide(coordinate, tipEl) {
  136. if (!this.kmap?.map || !coordinate?.length) return;
  137. this.drawNoRangeSquare(coordinate);
  138. if (tipEl) {
  139. if (!this.noRangeTipOverlay) {
  140. this.noRangeTipOverlay = new Overlay({
  141. element: tipEl,
  142. positioning: "top-center",
  143. offset: [0, 10],
  144. stopEvent: true,
  145. });
  146. this.kmap.map.addOverlay(this.noRangeTipOverlay);
  147. } else if (this.noRangeTipOverlay.getElement() !== tipEl) {
  148. this.noRangeTipOverlay.setElement(tipEl);
  149. }
  150. this.updateNoRangeTipPosition(coordinate);
  151. }
  152. }
  153. clearNoRangeGuide() {
  154. this.noRangeGuideLayer?.source?.clear?.();
  155. if (this.noRangeTipOverlay) {
  156. this.noRangeTipOverlay.setPosition(undefined);
  157. }
  158. }
  159. /** 当前地图点位(优先标记点,其次视图中心) */
  160. getCurrentCoordinate() {
  161. const features = this.clickPointLayer?.source?.getFeatures?.() || [];
  162. if (features.length) {
  163. return features[0].getGeometry()?.getCoordinates?.();
  164. }
  165. return this.kmap?.getView?.()?.getCenter?.() || mapLocation.data || null;
  166. }
  167. toPointWkt(coordinate) {
  168. if (!coordinate?.length) return null;
  169. return `POINT(${Number(coordinate[0])} ${Number(coordinate[1])})`;
  170. }
  171. // 地图点击事件
  172. addMapSingerClick() {
  173. let that = this;
  174. that.kmap.on("singleclick", (evt) => {
  175. // that.kmap.map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) {
  176. // if ( layer instanceof VectorLayer && layer.get("name") === "reportPolygonLayer" ) {
  177. // areaId.data = feature.get("id")
  178. // }
  179. // });
  180. that.setMapPoint(evt.coordinate)
  181. mapLocation.data = evt.coordinate
  182. });
  183. }
  184. clearLayer() {
  185. // this.kmap.removeLayer(this.clickPointLayer.layer)
  186. this.kmap.polygonLayer.source.clear();
  187. }
  188. createFarmPolygonStyle() {
  189. return new Style({
  190. fill: new Fill({
  191. color: "rgba(19, 162, 127, 0.25)",
  192. }),
  193. stroke: new Stroke({
  194. color: "#13a27f",
  195. width: 2,
  196. }),
  197. });
  198. }
  199. setAreaGeometry(geometryArr) {
  200. this.clearLayer();
  201. const format = new WKT();
  202. const mapProjection = this.kmap.map.getView().getProjection();
  203. geometryArr.forEach((item) => {
  204. const geometry = format.readGeometry(item, {
  205. dataProjection: "EPSG:4326",
  206. featureProjection: mapProjection,
  207. });
  208. const feature = new Feature({ geometry });
  209. feature.setStyle(this.createFarmPolygonStyle());
  210. this.kmap.polygonLayer.source.addFeature(feature);
  211. });
  212. this.scheduleFitView();
  213. }
  214. fitView(options = {}) {
  215. const map = this.kmap?.map;
  216. if (!map) return;
  217. map.updateSize();
  218. const extent = this.kmap.polygonLayer.source.getExtent();
  219. if (!extent || !isFinite(extent[0])) return;
  220. map.getView().fit(extent, {
  221. duration: options.duration ?? 0,
  222. padding: options.padding ?? [120, 48, 48, 48],
  223. maxZoom: options.maxZoom ?? 18,
  224. });
  225. }
  226. scheduleFitView(options = {}) {
  227. const run = () => this.fitView(options);
  228. requestAnimationFrame(() => {
  229. setTimeout(run, 50);
  230. });
  231. }
  232. }
  233. export default IndexMap;