|
|
@@ -0,0 +1,280 @@
|
|
|
+import * as KMap from "@/utils/ol-map/KMap";
|
|
|
+import * as util from "@/common/ol_common.js";
|
|
|
+import config from "@/api/config.js";
|
|
|
+import Style from "ol/style/Style";
|
|
|
+import Icon from "ol/style/Icon";
|
|
|
+import Fill from "ol/style/Fill";
|
|
|
+import Stroke from "ol/style/Stroke";
|
|
|
+import { Point } from 'ol/geom';
|
|
|
+import Feature from "ol/Feature";
|
|
|
+import * as proj from "ol/proj";
|
|
|
+import { getArea } from 'ol/sphere.js';
|
|
|
+import WKT from "ol/format/WKT.js";
|
|
|
+import GeoJSON from "ol/format/GeoJSON.js";
|
|
|
+import * as turf from "@turf/turf";
|
|
|
+import proj4 from "proj4"
|
|
|
+import { register } from "ol/proj/proj4";
|
|
|
+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");
|
|
|
+register(proj4);
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description 地图层对象
|
|
|
+ */
|
|
|
+class EditMap {
|
|
|
+ constructor() {
|
|
|
+ let that = this;
|
|
|
+ let vectorStyle = new KMap.VectorStyle();
|
|
|
+ this.vectorStyle = vectorStyle;
|
|
|
+
|
|
|
+ // 位置图标
|
|
|
+ this.clickPointLayer = new KMap.VectorLayer("clickPointLayer", 9999, {
|
|
|
+ style: (f) => {
|
|
|
+ return new Style({
|
|
|
+ image: new Icon({
|
|
|
+ src: require("@/assets/img/home/garden-point.png"),
|
|
|
+ scale: 0.5,
|
|
|
+ anchor: [0.5, 1],
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ // 小网格图层(约 1 亩参考网格)
|
|
|
+ this.gridLayer = new KMap.VectorLayer("gridLayer", 900, {
|
|
|
+ style: (feature) => {
|
|
|
+ const selected = !!feature.get("selected");
|
|
|
+ return new Style({
|
|
|
+ fill: new Fill({
|
|
|
+ color: selected ? "rgba(255, 64, 64, 0.45)" : "rgba(255, 255, 255, 0.08)",
|
|
|
+ }),
|
|
|
+ stroke: new Stroke({
|
|
|
+ color: selected ? "rgba(255, 64, 64, 0.95)" : "rgba(255, 255, 255, 0.4)",
|
|
|
+ width: selected ? 1.8 : 1,
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
+ this.selectedGridIds = new Set();
|
|
|
+ this.gridClickHandlerBound = false;
|
|
|
+ // 1 亩约 666.67m²,正方形边长约 25.82m
|
|
|
+ this.gridSideKm = 0.02582;
|
|
|
+ }
|
|
|
+
|
|
|
+ initMap(location, target) {
|
|
|
+ let level = 16;
|
|
|
+ let coordinate = util.wktCastGeom(location).getFirstCoordinate();
|
|
|
+ this.kmap = new KMap.Map(target, level, coordinate[0], coordinate[1], null, 8, 22);
|
|
|
+ let xyz2 = config.base_img_url3 + "map/lby/{z}/{x}/{y}.png";
|
|
|
+ this.kmap.addXYZLayer(xyz2, { minZoom: 8, maxZoom: 22 }, 2);
|
|
|
+ this.kmap.addLayer(this.clickPointLayer.layer);
|
|
|
+ this.kmap.addLayer(this.gridLayer.layer);
|
|
|
+ this.setMapPoint(coordinate)
|
|
|
+
|
|
|
+ this.kmap.initDraw((e) => {
|
|
|
+ this.rebuildGridFromPolygon();
|
|
|
+ })
|
|
|
+
|
|
|
+ // 监听drawstart事件,在开始绘制前检查是否已有地块
|
|
|
+ this.kmap.draw.on('drawstart', (e) => {
|
|
|
+ const features = this.kmap.getLayerFeatures();
|
|
|
+ if (features && features.length >= 1) {
|
|
|
+ // 提示用户先删除当前地块
|
|
|
+ this.onDrawLimit && this.onDrawLimit();
|
|
|
+ // 取消本次绘制
|
|
|
+ this.kmap.draw.abortDrawing();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this.kmap.startDraw()
|
|
|
+ this.kmap.modifyDraw(() => {
|
|
|
+ this.rebuildGridFromPolygon();
|
|
|
+ })
|
|
|
+ this.bindGridClick();
|
|
|
+ }
|
|
|
+
|
|
|
+ setAreaGeometry(geometryArr, needFitView = false) {
|
|
|
+ let that = this
|
|
|
+ geometryArr.map(item => {
|
|
|
+ // 不使用 setLayerWkt,而是手动添加要素,避免自动缩放视图
|
|
|
+ const format = new WKT()
|
|
|
+ const mapProjection = that.kmap.map.getView().getProjection()
|
|
|
+ let geometry = format.readGeometry(item, {
|
|
|
+ dataProjection: 'EPSG:4326',
|
|
|
+ featureProjection: mapProjection
|
|
|
+ })
|
|
|
+ let f = new Feature({ geometry: geometry })
|
|
|
+ that.kmap.polygonLayer.source.addFeature(f)
|
|
|
+ })
|
|
|
+ this.rebuildGridFromPolygon();
|
|
|
+ // 根据参数决定是否需要自适应地块范围
|
|
|
+ if (needFitView) {
|
|
|
+ this.fitView()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ fitView(){
|
|
|
+ let extent = this.kmap.polygonLayer.source.getExtent()
|
|
|
+ // 地图自适应到区域可视范围
|
|
|
+ this.kmap.getView().fit(extent, { duration: 500, padding: [100, 100, 100, 100] });
|
|
|
+ }
|
|
|
+
|
|
|
+ clearLayer(){
|
|
|
+ // this.kmap.removeLayer(this.clickPointLayer.layer)
|
|
|
+ this.kmap.polygonLayer.source.clear();
|
|
|
+ this.gridLayer.source.clear();
|
|
|
+ this.selectedGridIds.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ getAreaGeometry() {
|
|
|
+ const features = this.kmap.getLayerFeatures()
|
|
|
+ let geometryArr = []
|
|
|
+ let area = 0
|
|
|
+ const format = new WKT()
|
|
|
+ // 获取图层上的Polygon,转成WKT用于回显
|
|
|
+ features.forEach(item => {
|
|
|
+ // 使用 writeGeometry 而不是 writeFeature,因为 setLayerWkt 期望的是几何体的 WKT
|
|
|
+ const geometry = item.getGeometry()
|
|
|
+ geometryArr.push(format.writeGeometry(geometry, {
|
|
|
+ dataProjection: 'EPSG:4326',
|
|
|
+ featureProjection: this.kmap.map.getView().getProjection()
|
|
|
+ }))
|
|
|
+ let geom = geometry.clone()
|
|
|
+ geom.transform(proj.get("EPSG:4326"), proj.get("EPSG:38572"))
|
|
|
+ let areaItem = getArea(geom)
|
|
|
+ areaItem = (areaItem + areaItem / 2) / 1000;
|
|
|
+ area += areaItem
|
|
|
+ })
|
|
|
+ return {geometryArr, mianji: area.toFixed(2)} // 修改为 mianji 字段,与创建页面保持一致
|
|
|
+ }
|
|
|
+
|
|
|
+ setMapPosition(center) {
|
|
|
+ this.kmap.getView().animate({
|
|
|
+ center,
|
|
|
+ zoom: 17,
|
|
|
+ duration: 500,
|
|
|
+ });
|
|
|
+ this.setMapPoint(center)
|
|
|
+ }
|
|
|
+
|
|
|
+ setMapPoint(coordinate) {
|
|
|
+ this.clickPointLayer.source.clear()
|
|
|
+ let point = new Feature(new Point(coordinate))
|
|
|
+ this.clickPointLayer.addFeature(point)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除当前地块
|
|
|
+ deleteCurrentPolygon() {
|
|
|
+ this.kmap.polygonLayer.source.clear();
|
|
|
+ this.gridLayer.source.clear();
|
|
|
+ this.selectedGridIds.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置绘制限制回调
|
|
|
+ setDrawLimitCallback(callback) {
|
|
|
+ this.onDrawLimit = callback;
|
|
|
+ }
|
|
|
+
|
|
|
+ bindGridClick() {
|
|
|
+ if (!this.kmap?.map || this.gridClickHandlerBound) return;
|
|
|
+ this.gridClickHandlerBound = true;
|
|
|
+ this.kmap.on("singleclick", (evt) => {
|
|
|
+ let hitFeature = null;
|
|
|
+ this.kmap.map.forEachFeatureAtPixel(evt.pixel, (feature, layer) => {
|
|
|
+ if (layer === this.gridLayer.layer) {
|
|
|
+ hitFeature = feature;
|
|
|
+ return feature;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ });
|
|
|
+ if (!hitFeature) return;
|
|
|
+
|
|
|
+ const gridId = hitFeature.get("gridId");
|
|
|
+ const isSelected = !!hitFeature.get("selected");
|
|
|
+ hitFeature.set("selected", !isSelected);
|
|
|
+ if (isSelected) {
|
|
|
+ this.selectedGridIds.delete(gridId);
|
|
|
+ } else {
|
|
|
+ this.selectedGridIds.add(gridId);
|
|
|
+ }
|
|
|
+ hitFeature.changed();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ rebuildGridFromPolygon() {
|
|
|
+ if (!this.kmap?.polygonLayer?.source || !this.gridLayer?.source) return;
|
|
|
+ const features = this.kmap.polygonLayer.source.getFeatures();
|
|
|
+ this.gridLayer.source.clear();
|
|
|
+ this.selectedGridIds.clear();
|
|
|
+ if (!features || !features.length) return;
|
|
|
+
|
|
|
+ const format = new WKT();
|
|
|
+ const geojsonFormat = new GeoJSON();
|
|
|
+ const polygonGeometries = [];
|
|
|
+ features.forEach((feature) => {
|
|
|
+ const geometry = feature.getGeometry();
|
|
|
+ if (!geometry) return;
|
|
|
+ const wkt = format.writeGeometry(geometry, {
|
|
|
+ dataProjection: "EPSG:4326",
|
|
|
+ featureProjection: this.kmap.map.getView().getProjection(),
|
|
|
+ });
|
|
|
+ const turfGeom = turf.wktToGeoJSON ? turf.wktToGeoJSON(wkt) : null;
|
|
|
+ if (turfGeom) {
|
|
|
+ polygonGeometries.push(turfGeom);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 回退:用 OL 读为 GeoJSON geometry
|
|
|
+ const json = geojsonFormat.writeGeometryObject(geometry, {
|
|
|
+ dataProjection: "EPSG:4326",
|
|
|
+ featureProjection: this.kmap.map.getView().getProjection(),
|
|
|
+ });
|
|
|
+ polygonGeometries.push(json);
|
|
|
+ });
|
|
|
+ if (!polygonGeometries.length) return;
|
|
|
+
|
|
|
+ const multiPolygonCoords = [];
|
|
|
+ polygonGeometries.forEach((geom) => {
|
|
|
+ if (!geom) return;
|
|
|
+ if (geom.type === "Polygon") {
|
|
|
+ multiPolygonCoords.push(geom.coordinates);
|
|
|
+ } else if (geom.type === "MultiPolygon") {
|
|
|
+ geom.coordinates.forEach((coords) => multiPolygonCoords.push(coords));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (!multiPolygonCoords.length) return;
|
|
|
+
|
|
|
+ const farmPolygon = turf.multiPolygon(multiPolygonCoords);
|
|
|
+ const bbox = turf.bbox(farmPolygon);
|
|
|
+ const squareGrid = turf.squareGrid(bbox, this.gridSideKm, { units: "kilometers" });
|
|
|
+ if (!squareGrid?.features?.length) return;
|
|
|
+
|
|
|
+ squareGrid.features.forEach((gridFeature, index) => {
|
|
|
+ let clipped = null;
|
|
|
+ try {
|
|
|
+ clipped = turf.intersect(gridFeature, farmPolygon);
|
|
|
+ } catch (_) {
|
|
|
+ try {
|
|
|
+ // 兼容 turf 不同版本的 intersect 入参
|
|
|
+ clipped = turf.intersect(turf.featureCollection([gridFeature, farmPolygon]));
|
|
|
+ } catch (e) {
|
|
|
+ clipped = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!clipped) return;
|
|
|
+ const geometryObj = clipped.geometry;
|
|
|
+ if (!geometryObj) return;
|
|
|
+
|
|
|
+ const olGeometry = geojsonFormat.readGeometry(geometryObj, {
|
|
|
+ dataProjection: "EPSG:4326",
|
|
|
+ featureProjection: this.kmap.map.getView().getProjection(),
|
|
|
+ });
|
|
|
+ if (!olGeometry) return;
|
|
|
+ const cell = new Feature({ geometry: olGeometry });
|
|
|
+ cell.set("gridId", `grid-${index}`);
|
|
|
+ cell.set("selected", false);
|
|
|
+ this.gridLayer.source.addFeature(cell);
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default EditMap;
|