An overview,

Following up on the previous article, here is the function of line drawing. Because there are no related classes for layer rendering in Leaflet, there is no clear division of the concepts of Geometry and Graphic. This makes the code much simpler when it comes to completing the image function. The key to the drawing of ** points, lines and planes mentioned in the previous article is to obtain the correct coordinates according to the operation of users and organize these coordinates into different element types of data. ** What is common in the drawing of the three types of elements is that user operations on the map must be monitored, while the difference lies in the response to user operation results.

I. Functional analysis

The complete flow of line drawing function in drawing is shown in the figure above. There are a few places, according to my understanding and design of the function, I think the following places need to be briefly explained.

1. Rubber band effect

The rubber band effect is actually a live preview of the existing node connected to the current mouse position before a line segment is drawn. Actually, this effect is not necessary. In order to be more user-friendly, almost all of the rendering capabilities, including 2d and 3D GIS platforms, will provide this effect.

2. Cancel and finish drawing

Cancel is the end before the user does not want to keep the current result and push out the drawing state.

After the drawing is complete, keep the current drawing result and do not exit the drawing state.

* This refers to my feature design as you will see.

Two, code implementation

* All code was added or modified in the project structure of Chapter 1.

The code is still in mapdraw.vue, map.js, and the newly created Polyline.vue mentioned in the previous article. Take a look at all the code and then explain some parts. In order to match the source code with the article, I have temporarily blocked the Point and Polygon.

// src\components\MapDraw.vue

<template>
  <div class="map-tools">
    <ul>
      <li :class="[{active: activeTool == 'point'}]" @click="point">Point</li>
      <li :class="[{active: activeTool == 'polyline'}]" @click="polyline">Polyline</li>
      <li :class="[{active: activeTool == 'polygon'}]" @click="polygon">Polygon</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "mapDraw".data() {
    return {
      activeTool: ""
    };
  },
  methods: {
    point() {
      if(this.activeTool ! = ="point") {
        this.activeTool = "point";
        this.$emit("point");
      } else {
        this.activeTool = "";
        this.$emit("end"); }},polyline() {
      if(this.activeTool ! = ="polyline") {
        this.activeTool = "polyline";
        this.$emit("polyline");
      } else {
        this.activeTool = "";
        this.$emit("end"); }},polygon() {
      if(this.activeTool ! = ="polygon") {
        this.activeTool = "polygon";
        this.$emit("polygon");
      } else {
        this.activeTool = "";
        this.$emit("end"); }}}}; </script> <style lang="less"> .map-tools { position: absolute; right: 15px; top: 15px; z-index: 999; height: 36px; Box-shadow: 0px 0px 50px 2px Rgba (0, 0, 0, 0.35); background-color:#fff;
  ul {
    display: flex;
    padding: 0;
    margin: 0;
    list-style: none;

    li {
      padding: 0 15px;
      height: 36px;
      font-size: 13px;
      line-height: 36px;
      cursor: pointer;
    }
    li.active {
      background-color: rgb(102, 156, 255);
      color: #fff;
    }
    li:hover {
      background-color: rgb(212, 224, 246);
    }
  }
}
</style>

Copy the code

2) Add a new view polyline.vue

// src\views\Polyline.vue
<template>
  <div class="map-container">
    <div id="map-container"></div>
    <MapDraw @point="{}" @polyline="drawPolyline" @polygon="{}" @end="drawOff"></MapDraw>
  </div>
</template>

<script>
import MapDraw from "@/components/MapDraw.vue";

export default {
  name: "map-point",
  components: { MapDraw },
  data() {
    return {
      map: null,
      OSMUrl: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
      overLayer: {
        polylines: []
      },
      tempGp: {
        lineNode: [],
        lineNodeLen: 0,
        tempLine: null,
        tempNode: []
      }
    };
  },
  mounted() {
    this.map = this.$utils.map.createMap("map-container");
    this.$utils.map.createTileLayer(this.map, this.OSMUrl, {}); This. Map. SetView ((51.505, 0.09), 13); }, methods: {drawOn() {// Remove the listener map event this.map.off("click");
      this.map.off("mousemove");
      this.map.off("dblclick");
      this.map.doubleClickZoom.disable();
    },
    drawOff() {// Remove the listener map click event this.map.off("click");
      this.map.off("mousemove");
      this.map.off("dblclick"); this.map.doubleClickZoom.enable(); // Undo the mouse pan style this.$utils.map.removerCoursorStyle(this.map);
    },
    drawPolyline() {
      this.$utils.map.addCursorStyle(this.map, "crosshare-cursor");
      let tempPolygonOpts = {
        color: "Rgba (255, 0, 0, 0.85)"Opacity: 0.85};let finalPolygonOpts = {
        color: "Rgba (0, 255, 0, 0.85)"Opacity: 0.85}; this.drawOn(); this.map.on("click", evt => {
        this.tempGp.lineNode.push([evt.latlng.lat, evt.latlng.lng]);
        this.tempGp.tempNode.push(this.addNode(evt.latlng, this.map));
        this.tempGp.lineNodeLen = this.tempGp.lineNode.length;
      });

      this.map.on("mousemove", evt => {
        if (this.tempGp.lineNodeLen >= 1) {
          if (this.tempGp.tempLine) this.tempGp.tempLine.remove();
          this.tempGp.lineNode[this.tempGp.lineNodeLen] = [
            evt.latlng.lat,
            evt.latlng.lng
          ];

          this.tempGp.tempLine = this.$utils.map.createPolyline( this.map, this.tempGp.lineNode, tempPolygonOpts ); }}); this.map.on("dblclick", () => {
        this.overLayer.polylines.push(
          this.$utils.map.createPolyline(
            this.map,
            this.tempGp.lineNode,
            finalPolygonOpts
          )
        );
        this.tempGp.lineNode = [];
        this.tempGp.lineNodeLen = 0;
        this.tempGp.tempLine.remove();
        this.tempGp.tempNode.map(el => el.remove());
      });
    },
    addNode(latlng, map) {
      let node = this.$utils.map.createIcon({
        iconUrl: require(". /.. /assets/images/node.png"),
        iconSize: [16, 16]
      });
      node = this.$utils.map.createMakerByLatlng(latlng, {
        icon: node
      });
      node.addTo(map);
      returnnode; }}}; </script> <style lang="less">
.map-container {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;

  #map-container {
    width: 100%;
    height: 100%;
  }
}
</style>

Copy the code

All the code for the lines drawn above is actually quite simple to implement. I’m going to pick out some of the things that I think need explaining,

1) Temporary data in data

In fact, the process of drawing is the process of constantly deleting lines and adding lines. As long as the coordinate changes are monitored, the previous graph will be deleted and a new graph will be drawn.

Therefore, intermediate data for auxiliary purposes must be generated during the drawing process, which needs to be defined in advance in data

. overLayer: {polylines: []   // Store the line object generated after the final drawing
},
tempGp: {
    lineNode: [].// The coordinate string used to generate the line graph during the drawing process
    lineNodeLen: 0.// Number of added nodes
    tempLine: null.// Draw line objects on the map before completion
    tempNode: []    // The node object generated by each click}...Copy the code

2) Draw code description

drawPolyline() {
    this.$utils.map.addCursorStyle(this.map, "crosshare-cursor");
    // The line style in the drawing process
    let tempPolygonOpts = {
        color: "Rgba (255, 0, 0, 0.85)".weight: 3.opacity: 0.85
    };
    // The finished line style can be the same as the line style in the drawing process above, but the color is used here for differentiation
    let finalPolygonOpts = {
        color: "Rgba (0, 255, 0, 0.85)".weight: 3.opacity: 0.85
    };

    this.drawOn();
    // Each click counts as adding a node to the line the user wants to draw
    this.map.on("click", evt => {
        this.tempGp.lineNode.push([evt.latlng.lat, evt.latlng.lng]);
        this.tempGp.tempNode.push(this.addNode(evt.latlng, this.map));
        // Update the number of added nodes
        this.tempGp.lineNodeLen = this.tempGp.lineNode.length;
    });

    this.map.on("mousemove", evt => {
        // Only the first node will appear on the map when clicked for the first time
        // From the second start to the end of the drawing, no matter how many points the user adds, the mouse movement needs to follow the new one
        // the n+1 of this. TempGp. LineNode does this to achieve the rubber band effect
        if (this.tempGp.lineNodeLen >= 1) {
            // Remove the previous graph
            if (this.tempGp.tempLine) this.tempGp.tempLine.remove();
            // The index of the n+1 point is this.tempgp.linenodelen +1
            // This. TempGp. LineNodeLen + 1 is the same as long as the next click occurs
            this.tempGp.lineNode[this.tempGp.lineNodeLen] = [
                evt.latlng.lat,
                evt.latlng.lng
            ];
            // Draw a new temporary graph
            this.tempGp.tempLine = this.$utils.map.createPolyline(
                this.map,
                this.tempGp.lineNode, tempPolygonOpts ); }});// Double-click the map to finish drawing
    this.map.on("dblclick", () = > {this.overLayer.polylines.push(
            this.$utils.map.createPolyline(
                this.map,
                this.tempGp.lineNode,
                finalPolygonOpts
            )
        );

        // Reset temporary data
        this.tempGp.lineNode = [];
        this.tempGp.lineNodeLen = 0;
        this.tempGp.tempLine.remove();
        this.tempGp.tempNode.map(el= > el.remove());
    });
}
Copy the code

End result:

3) the summary

The draw line function itself is very simple, beginners are most likely to get stuck in the implementation of the rubber band effect on this step. Before realizing the above functions, it is necessary to have a certain understanding of the constructors, attributes, events and other basic information of objects such as Map, marker and polyline in Leaflet. At the same time, we must make clear the realization of the train of thought, so that we can draw inferences from one another, and then deal with similar functions skillfully.




directory

Vue-cli and Leaflet: Start using the Leaflet in VUe-CLI

(2) VUE-CLI and Leaflet: basic map operations (zoom in, zoom out, translation, positioning, etc.)

Vue-cli and Leaflet: Add marker, polyline, polygon

Vue-cli and Leaflet: Add tooltips and popup

Vue-cli and Leaflet: point drawing

Vue-cli and Leaflet: line drawing

(7) VuE-CLI and Leaflet: surface painting system

Vue-cli and Leaflet: Load Esri ArcGIS Map Service

(9) VUE-CLI and Leaflet: layer control implementation of basic functions

(10) VUE-CLI and Leaflet: AGS attribute query and point map query

Vue-cli and Leaflet: point aggregation leaflet. markerCluster

Please refer to my GitHub for the source code. Since the article is written at the same time as coding, the source code in GitHub may be a little confused, you can find the corresponding code according to the function. Later will be sorted out and improved.