“This is the third day of my participation in the November Gwen Challenge. See details of the event: The last Gwen Challenge 2021”.

One, foreword

Due to the recent needs of the project, I need to use OpenLayers in the VUE project to develop GIS map. The article on OpenLayers is not too big on the Internet, so I take this opportunity to share some of my experience in the actual use of the project.

This series will share some of the feature points implemented over the course of the project. Past Catalogue:

  1. Vue +OpenLayers Project Practice (I) : Basic drawing and clicking pop-ups

  2. Vue +OpenLayers Project Practice (2) : Switching between multiple maps

In the actual project, we often need to batch to draw on the map coordinates, tag, if mark too much, will inevitably cause rendering time is too long, and a series of problems, and at the time of the relatively small zoom is unfavorable to check, so the official API provides a Cluster: Cluster, this paper is the use of simple talk about the process.

You can refer to the official example

Second, concrete implementation

First, analyze the functions we need to achieve:

  1. Draw a large number of coordinate points;
  2. Set the cluster, through zooming can display different cluster UI;
  3. Click the cluster point to enlarge the display;
  4. Other optimization;

1. Draw coordinate points

Just a few adjacent points are used here for demonstration purposes, changing the previous home.vue code

/ / data to add
data() {
  return{...markerLayer: null.// Coordinate marker layer
    markerSource: null.// Coordinate data source
  };
},
Copy the code

Encapsulate the point data and VectorSource, add getPoints method and setMarkerSource method:

// demonstrate the direct static return coordinate array, the actual project can be changed to interface to get data
getPoints() {
  return[[108.945951.34.465262],
    [109.04724.34.262504],
    [108.580321.34.076162],
    [110.458983.35.071209],
    [105.734862.35.49272]]; },setMarkerSource() {
  let _style = new Style({
    image: new sCircle({
      radius: 10.stroke: new Stroke({
        color: "#fff",}).fill: new Fill({
        color: "#3399CC",})})});let _points = this.getPoints();
  let _features = _points.reduce((list, item) = > {
    let _feature = new Feature({
      geometry: new Point(olProj.fromLonLat(item)),
    });
    _feature.setStyle(_style);
    list.push(_feature);
    returnlist; } []);this.markerSource = new VectorSource({
    features: _features,
  });
}
Copy the code

Modify the previous setMarker method:

setMarker() {
  this.setMarkerSource();
  this.markerLayer = new VectorLayer({
    source: this.markerSource,
  });
  this.openMap.addLayer(this.markerLayer);
}
Copy the code

At this point, five coordinate points have been successfully drawn on the map

2. Set the cluster

Here we add the Cluster, first changing the previous setMarkerSource method

setMarkerSource(){...this.markerSource = new Cluster({
    distance: 100.source: new VectorSource({
      features: _features,
    }),
  });
}
Copy the code

After refreshing, you can see that the aggregation function has been implemented, but the previous point style is missing. We need to adjust the point style. Create a new style method setClusterStyle:

setClusterStyle() {
  const styleCache = {};
  const _style = (feature) = > {
    const size = feature.get("features").length;
    let style = styleCache[size];
    if(! style) { style =new Style({
        image: new sCircle({
          radius: 10.stroke: new Stroke({
            color: "#fff",}).fill: new Fill({
            color: "#3399CC",})}),text: new Text({
          text: size.toString(),
          fill: new Fill({
            color: "#fff",})})}); styleCache[size] = style; }return style;
  };
  return _style;
},
Copy the code

This takes the form of a closure that caches size. Now modify the setMarker method to add a style and remove the style Settings from the setMarkerSource method:

setMarker() {
  this.setMarkerSource();
  let _style = this.setClusterStyle();
  this.markerLayer = new VectorLayer({
    source: this.markerSource,
    style: _style,
  });
  this.openMap.addLayer(this.markerLayer);
}
Copy the code

Ok, the style has been added successfully, but here we want the non-converged dots not to show numbers and the style to be different from the converged dots. Modify the code:

setClusterStyle() {
  const styleCache = {};
  const _style = (feature) = > {
    const size = feature.get("features").length;
    let style = styleCache[size];
    if(! style) {if (size > 1) {
        style = new Style({
          image: new sCircle({
            radius: 20.stroke: new Stroke({
              color: "#fff",}).fill: new Fill({
              color: "#3399CC",})}),text: new Text({
            text: size.toString(),
            fill: new Fill({
              color: "#fff",})})}); }else {
        style = new Style({
          image: new sCircle({
            radius: 15.stroke: new Stroke({
              color: "#fff",}).fill: new Fill({
              color: "#e9b626",})})}); } styleCache[size] = style; }return style;
  };
  return _style;
}
Copy the code

Size is used directly to determine whether it is an aggregate, and then different styles are set separately.

Set (“key”,”value”,” Boolean “) when creating a feature at each point. Then judge the Settings by item.get(“key”), and you can study them by yourself.

3. Cluster click events

OK, next we need to zoom in when clicking on the aggregation point and modify the previous click event singleclick

singleclick() {
  / / click
  this.openMap.on("singleclick".(e) = > {
    this.markerLayer.getFeatures(e.pixel).then((clickedFeatures) = > {
      if (clickedFeatures.length) {
        const features = clickedFeatures[0].get("features");
        if (features.length > 1) {
          const extent = boundingExtent(
            features.map((r) = > r.getGeometry().getCoordinates())
          );
          this.openMap
            .getView()
            .fit(extent, { duration: 1000.padding: [200.200.200.200]}); }else {
          this.shopPopup = true;
          // Set the popover position
          let coordinates = features[0].getGeometry().getCoordinates();
          this.popup.setPosition(coordinates); }}else {
        this.shopPopup = false; }}); }); },Copy the code

A new class boundingExtent is referenced here, which is an array of coordinates obtained by features.map((r) => r.getGeometry().getCoordinates()) and then scaled by fit(). That is, zoom to maximize the display of all points inside the aggregate, while keeping 200 padding at the edge.

Instead of aggregating the point point click to show the popover that we had before.

4. Other optimizations

At this point, we find that when we click on the show popover and zoom, the popover does not disappear, the effect is not ideal, so we need to listen to zoom and hide the popover. Add listener change:resolution on getView()

resolutionChange() {
  // Listen for scaling
  this.openMap.getView().on("change:resolution".(e) = > {
    console.log(e);
    this.shopPopup = false;
  });
}
Copy the code

The last

Gitee address

Thank you for reading

It would be a great honor if I could help you.

In the future, more functions will be updated, as well as some problems encountered in the project, interested partners can click favorites/follow.