The final result

Technology stack

The technology used in the project is Autonavi’s VUE-AMap based on VUE, the component uses Element, and the algorithm is turf.js

Configuration Procedure Division

1. Create vUE files and introduce VUE-AMAP into the project. There are two official introduction methods, one is NPM installation and the other is CDN introduction

Official document: VuE-AMap

2. According to the steps of the document, import in main.js and import in the page, and then you can use the official components. There are three components mainly used here, one map component el-amap and one point coordinate el-amap-marker. A polygon covering el-amap-polygon(the main implementation component) is a polygon

3. The JS algorithm of the map is Turf. js, turf is a JS library for spatial analysis. It includes traditional spatial manipulation, auxiliary functions for creating GeoJSON data, and data classification and statistics tools. Turf can be added to your site as a client plug-in, or you can use it on the Node.js development back end. For those interested, check out Github, a great library for developing maps

After downloading it using NPM (NPM install @turf/turf), import its js file in the page and assign it to a variable (const turf = require(‘@turf/turf’);) .

Implementation approach

1. Listen to the click on the page, obtain the coordinates of the current click point, and then store the longitude and latitude in data through the events property bound by the map component. In the bound property object, listen to click events and save the events

2. Monitor the change of LNG or LAT in data through VUE watch. When the value changes, new points will be added to the page

3. Monitor the number of points on the page to see if the length is greater than three, and use the el-amap-polygon to draw more points

But here there is a problem, that is, if the page click on the coordinates of the point of order will not be able to make a mark of the polygon, this how to judge, because el - amap - polygon component is according to your polygon coordinates in order to construct the array, for example, a rectangle, if the point order is not spinning clockwise or counterclockwise, The result is a triangle with two intersecting vertices, in which case you need to use Turf to determine whether the array's points are in clockwise or counterclockwise order

4. Go through the TurfkinksMethod to determine whether the polygon is a legitimate polygon, that is, whether there is self-intersection

After these four steps, a polygon is drawn on the page

The idea of grid splitting

Have polygons drawn above, have to do now is to big polygon is divided into a multilateral type, a little the thinking of this step is to first get a quadrilateral surrounded on the outside of the polygon, then split the quadrangle, draw a small square, and then judge whether each square with the picture of whether the polygon intersection, Then cut out the intersecting parts to display, this step is divided into five steps

The first step is to determine whether the size of the polygon is in accordance with the rules

If you draw a large polygon, for example, including the whole of China, if you divide it by the area of one kilometer, it will require a huge number of computations, requiring high performance, and the low performance may be directly stuck. This array is controlled by vUE's environment variables Then, according to my business logic, I will determine whether the area of this polygon needs to be split or whether the user chooses to split it as shown in the figure below When the user clicks OK to continue, click Cancel to end the task

The second step is to take the outer polygons that cover the drawing polygons and break them into smaller polygons

This step uses bboxPolygon to get the quadrilateral that covers the edges of the drawn polygon

SquareGrid has three parameters, the first is bbox, the second is the programming required to split into small squares, and the third is optional, including what units to use (miles, kilometers, etc.).

The third step is to cut the small mesh and get the parts that coincide with the drawn polygon

Use INTERSECT to get the clipped grid

let clipped = turf.intersect(obj, polygon);

The fourth part, call painting function, painting a small grid

This. PolygonArr [index] = {draggable: false, strokeColor: '#49B2FF', fillColor: '#47ABF5', fillOpacity: 0.5, path: []}. / / the style of the polygon let coordinates = clipped. Geometry. Coordinates [0]. let coordinatesLeng = coordinates.length; for (let j = 0; j < coordinatesLeng - 1; j++) { this.polygonArr[index].path[j] = coordinates[j]; }Copy the code

At this point, a polygon split has been created, but there is a problem with this split, as shown below

Through the above four paintings, it will be found that the split polygon is not completely split, this is because squareGrid is divided outward through the center point, so if the external area is not enough to split a grid, the above situation will occur, so we will solve the problem through the fifth step

The fifth step is to solve the problem of incomplete separation of the part overlaps with the drawn polygon. Since the remaining area of the current drawn polygon is not enough to continue the separation when it reaches the outermost layer due to the area problem, we will expand the area of the polygon wrapped outside to expand it, isn’t that ok? So we need to use transformScale to expand its area, and then we’re done

/** * Zoom in on the outer polygon, @param {Object} polygon * @param {number} value * @param {number} sides */ acoverage(polygon, value, sides) { this.polygonArr.splice(this.polygonArr.length - len, len); let bbox = turf.bbox(polygon); console.log('bbox'); console.log(bbox); //bboxPolygon Takes a bbox and returns an equivalent polygon. let bboxPolygon = turf.bboxPolygon(bbox).geometry.coordinates[0]; // Get the peripheral quadrilateral console.log(bboxPolygon) that covers the drawn polygon; let polygon2 = turf.polygon([bboxPolygon], { name: 'poly2' }); console.log('polygon2', polygon2); let scaledPoly = turf.transformScale(polygon2, sides); // Enlarge the outer polygon console.log('scaledPoly', scaledPoly); let bbox2 = turf.bbox(scaledPoly); console.log(bbox2); let options = { units: 'kilometers', mask: scaledPoly }; let squareGrid = turf.squareGrid(bbox2, value, options); Console. log('squareGrid'); console.log(squareGrid); this.check(squareGrid.features, polygon); },Copy the code

The final result

Key steps complete code

@param {Object} Polygon turfpolygon type */ isIntersect(Polygon) {var kinks = turf.kinks(polygon); If (kinks.features.length > 0) {this.$message({showClose: true, message: 'error', type: 'error'}); this.obliterate(); } else {// There is no self-intersection area = Turf. area(polygon) / 1000000; console.log(area); console.log(process.env.VUE_APP_MAP_AREA); If (area < process.env.vue_app_map_area) {area > 1? this.promptArea(polygon) : this.noPromptArea(); } else {this.$message({message: 'drawing area too large ', type: 'warning'}); }}}, /** * Select polygon does not need to split */ noPromptArea() {this.tasKNauticaarr [0] = {flight_task_name: 'Task 1', task_coordinate_points: this.blockNautica }; }, /* promptArea(polygon) {this.polygonArr = []; This.$prompt(' This test area is large, do you want to split? ', 'confirmButtonText ', {confirmButtonText:' confirm ', cancelButtonText: 'cancel ', // inputPattern: /^[+]? [1-9][1-9]*$/, inputPattern: /^[+]? [1-9]$/, inputErrorMessage: 'Please input a positive integer greater than 0 and less than 10, unit square km'}). Then (({value}) => {let sides = 1; If (value <= 2) {sides = 1.4; } else if (value <= 6) {sides = 1.6; } else { sides = 2; } this.acoverage(polygon, value, sides); }) .catch(err => { console.log(err); This.$message({type: 'info', duration: 1500, message: 'unsplit'}); this.noPromptArea(); }); }, /** * Zoom in on the outer polygon, @param {Object} polygon * @param {number} value * @param {number} sides */ acoverage(polygon, value, sides) { this.polygonArr.splice(this.polygonArr.length - len, len); let bbox = turf.bbox(polygon); console.log('bbox'); console.log(bbox); //bboxPolygon Takes a bbox and returns an equivalent polygon. let bboxPolygon = turf.bboxPolygon(bbox).geometry.coordinates[0]; // Get the peripheral quadrilateral console.log(bboxPolygon) that covers the drawn polygon; let polygon2 = turf.polygon([bboxPolygon], { name: 'poly2' }); console.log('polygon2', polygon2); let scaledPoly = turf.transformScale(polygon2, sides); // Enlarge the outer polygon console.log('scaledPoly', scaledPoly); let bbox2 = turf.bbox(scaledPoly); console.log(bbox2); let options = { units: 'kilometers', mask: scaledPoly }; let squareGrid = turf.squareGrid(bbox2, value, options); Console. log('squareGrid'); console.log(squareGrid); this.check(squareGrid.features, polygon); }, /** * add wait for the tip, loop drawing method * @param {Array} arr * @param {Object} polygon */ check(arr, polygon) {// loop function, // console.log(arr); const loading = this.$loading({ lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)'}); len = arr.length; this.taskNauticaArr = []; for (let i = 0; i < len; i++) { this.cropdef(arr[i], polygon); } loading.close(); }, /** * clipping function, @param {Object} obj * @param {Object} polygon */ cropdef(obj, polygon) { let clipped = turf.intersect(obj, polygon); / / cut out each small grid intersecting polygons revealed the if replacement parts (clipped) {let clippedArr = clipped. Geometry. Coordinates [0]. this.addDrawPolygon(clipped); }}, /** * @param {Object} clipped */ addDrawPolygon(clipped) {let index = this.polygonarr.length; This. PolygonArr [index] = {draggable: false, strokeColor: '#49B2FF', fillColor: '#47ABF5', fillOpacity: 0.5, path: []}. / / the style of the polygon let coordinates = clipped. Geometry. Coordinates [0]. let coordinatesLeng = coordinates.length; for (let j = 0; j < coordinatesLeng - 1; j++) { this.polygonArr[index].path[j] = coordinates[j]; }}Copy the code

Watch monitors the latitude and longitude change of the point clicked by the mouse

*/ LNG () {if (this.alertshow! = false) { let obj = { position: [this.lng, this.lat], events: {}, visible: true, draggable: false, template: '<div style="width: 8px; height: 8px; background: #000; border: 2px solid #49B2FF; border-radius: 50%; margin: 28px 0 0 6px"></div>' }; this.markers.push(obj); if (this.markers.length > 3 && this.alertShow ! = false) { this.polygons[indexes] = { draggable: false, strokeColor: '#49B2FF', fillColor: '#47ABF5', fillOpacity: 0.5, the path: []}; let arr = []; // Let lengthNum = this.markers. Length; for (let i = 0; i < lengthNum; i++) { this.polygons[indexes].path[i] = this.markers[i].position; arr[i] = this.markers[i].position; } arr.push(this.markers[0].position); let polygon = turf.polygon([arr], { name: 'poly1' }); this.isIntersect(polygon); } else { } } } }Copy the code