2
头图

看过之前的文章,对于OpenLayers会有一个大致的了解。下面我们继续讲解 OpenLayer如何创建点标记。

下面示例是以 OpenLayersV8.2.0版本 作为依赖包

点标记

在地图上经常会打点上图,这是一个常见业务场景需求。像 打点标记,矢量边界线,点位提示窗等,也会经常需要,这些我们后面都会说道,现在来看看,OpenLayer中点标记如何上图。

OpenLayer中点标记(Marker)属于矢量图形,即矢量点。GIS地图上,日常开发上图的点、线、面、几何体都属于矢量图形。由于点标记的使用最为频繁常见,所以将它单独拿出来讲解。

OpenLayer中,点标记创建需要 FeatureGeometryStyle三者共同组合完成。Feature作为点标记的实体,Geometry创建几何体(点、线、圆、多边形),Style创建样式(颜色、粗细、文本等)。

当然,点标记创建完成后,需要添加到矢量图层,如果只创建不添加,就类似于只创建DOM节点,但是不append到是视图上。(点标记添加实际流程:需要先添加到矢量Source的要素上,Layer再使用矢量数据源)

下面我们来看看关于创建点标记的API和示例。
官方示例:Icon Point

Feature

  1. [ol.Feature](https://openlayers.org/en/v8.2.0/apidoc/module-ol_Feature-Feature.html):具有几何图形和其他属性特性的地理要素的矢量对象

    // 创建要素对象
    const Feature = new ol.Feature({
      // 创建geometry
      geometry: new ol.geom.Point([116.39, 39.91]),
      name: 'Marker'
    })

    Feature要素是地图上的可交互对象,可以是点、线、面等。要素需要添加到矢量图层中,才可展示。

    Geometry

    GeometryFeature

  2. ol/geom/Geometry:Abstract base class;
  3. ol.geom.Circle:Circle geometry;
  4. ol.geom.LineString:Linestring geometry;
  5. ol.geom.LinearRing:Linear ring geometry. Only used as part of polygon; (无法单独使用);
  6. ol.geom.MultiLineString:Multi-linestring geometry;
  7. ol.geom.MultiPoint:Multi-point geometry.
  8. ol.geom.MultiPolygon:Multi-polygon geometry
  9. ol.geom.Point:Point geometry;
  10. ol.geom.Polygon:Polygon geometry;

    // 创建多边形 polygon
    const points = [
      [
     [121.478815, 32.24161],
     [120.160318, 31.293255],
     [118.79129166, 32.06046152],
     [121.478815, 32.24161]
      ]
    ];
    var feature = new ol.Feature({
      geometry: new ol.geom.Polygon(points)
    });
    
    // 创建 MultiLine
    const points = [
      [
     [122.860194, 24.761729],
     [122.713016, 23.952719],
     [121.977124, 21.632922]
      ],
      [
     [125.509405, 24.896053],
     [125.509405, 24.896053],
     [124.11121, 21.632922]
      ]
    ];
    var feature = new ol.Feature({
      geometry: new ol.geom.MultiLineString(points)
    });

    Geometry是用于创建几何体,创建Feature要素时,可以指定Geometry

    Style

    Style即可直接在要素(Feature)设置,也可以在矢量数据源(Source)上设置。

  • ol.style.Style:矢量要素渲染样式的容器;
  • Fill:设置矢量要素的填充样式;
  • Icon:设置矢量要素的icon样式;
  • Text:设置矢量要素的文本样式;
  • Circle:设置矢量要素的圆形样式;
  • Stroke:设置矢量要素的描边样式;
  • RegularShape:设置矢量要素的常规形状样式;

    const iconStyle = new ol.style.Style({
      image: new ol.style.Icon({
          anchor: [0.5, 0.5],
          color: 'red',
          src: './img/point.png',
          width: 50
      })
    })

    官方示例:Icon PointMarker Animation

    示例

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <title>访问 Map</title>
      <link rel="stylesheet" href="./8.2.0/theme/ol.css">
      <script src="./8.2.0/en/latest/ol/dist/ol.js"></script>
      <!-- 不想下载就直接用下面的 -->
      <!-- <script src="https://cdn.jsdelivr.net/npm/ol@v8.2.0/dist/ol.js"></script>
          <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v8.2.0/ol.css"> -->
    </head>
    
    <body>
      <div id="map" style="height: 95vh; border: 6px solid;"></div>
    
      <script>
          const coordinate = [
              [
                  116.38390602111816,
                  39.91933196682435
              ],
              [
                  116.40862525939941,
                  39.89048282729144
              ]
          ]
          let map = new ol.Map({
              target: 'map',
              layers: [
                  new ol.layer.Tile({
                      // source: new ol.source.OSM()
                      source: new ol.source.BingMaps({
                          key: 'Aud1dM5V1tT5Yvg2uX8Hp5ftaZZ4stEuJZ8i7IO1KSyLFj9A4UuamHK6_tUKEcGe',
                          imagerySet: 'Road'  // 指定加载的图层名,还可以替换为'AerialWithLabels' 'Aerial'或'Road'
                      })
                  }),
              ],
              view: new ol.View({
                  center: [116.39, 39.91],
                  zoom: 13,
                  projection: 'EPSG:4326'
              })
          });
          // 地图点击事件
          map.on('click', (e) => {
              console.log(e);
          })
    
          // 创建Style
          const iconStyle = new ol.style.Style({
              image: new ol.style.Icon({
                  anchor: [0.5, 0.5],
                  color: 'red',
                  src: './img/point.png',
                  width: 50
              }),
              text: new ol.style.Text({
                  text: '点标记',
                  offsetY: -35,
                  fill: new ol.style.Fill({color: '#c6287b'})
              })
          })
          // 创建Feature要素
          const features = coordinate.map((item) => {
              const Feature = new ol.Feature({
                  // 创建geometry
                  geometry: new ol.geom.Point(item),
                  name: 'Marker'
              })
                // Style设置给Feature,也可直接放到Vector图层上
              Feature.setStyle(iconStyle);
              return Feature;
          })
          // 创建Vector图层
          const vectorLayer = new ol.layer.Vector({
              source: new ol.source.Vector(),
              // style: iconStyle
          })
            // Feature要素添加到Vector图层
          features.forEach((item) => vectorLayer.getSource().addFeature(item))
            // Vector图层添加到地图
          map.addLayer(vectorLayer);
            // 点标注点击
          map.on('click', (evt) => {
              const Feature = map.forEachFeatureAtPixel(evt.pixel, (feature) => {
                  return feature
              })
              if(Feature && Feature.get('name') === 'Marker') {
                  console.log('Marker点标注');
              }
          })
      </script>
    </body>
    </html>

    创建好Feature对象后,如果想上图,需要先将其添加到矢量Source上,SourceLayer是对应的,再将矢量Source添加到图层上,最好图层添加到地图上,要素(Feature)就可以展示啦!
    下面我们来预览效果吧!
    image.png

点聚合

看完点标记之后,我们矢量图形上图的基本模式了解了,下面我们一起来看看点聚合怎么搞!

Cluster

首先我们想要点聚合,就不能不用矢量图层(Vector),因为点聚合也是失恋数据图形。既然是聚合,数据源(Source)当然要使用聚合。ol.source.Cluster会对给到的矢量数据进行聚合,我们只需要提供矢量数据。

const source = new ol.source.Vector();
for(let i = 0; i < 200;i++) {
  const coordinate = [116.39+Math.random(), 39.91+Math.random()];
  const Feature = new ol.Feature(new ol.geom.Point(coordinate));
  source.addFeature(Feature);
}

for(let i = 0; i < 200;i++) {
  const coordinate = [116.40+Math.random(), 39.92+Math.random()];
  const Feature = new ol.Feature(new ol.geom.Point(coordinate));
  source.addFeature(Feature);
}

// 聚合
const Cluster = new ol.source.Cluster({
  source: source,
  distance: 100
})

Style

聚合数据源 + 要素 准备好之后,就只差要素的样式和上图了。下面来看看!

const Style = (options) => new ol.style.Style(options);
const Fill = (options) => new ol.style.Fill(options);
const Circle = (options) => new ol.style.Circle(options);
const Text = (options) => new ol.style.Text(options);
const Stroke = (options) => new ol.style.Stroke(options);
const Icon = (options) => new ol.style.Icon(options);
// 创建ClusterStyle
const createClusterStyle = (size) => {
    console.log(size);
    return Style({
        image: Circle({
            radius: 30,
            stroke: Stroke({
                color: '#fff'
            }),
            fill: Fill({
                color: 'rgba(0,0,0, 0.7)'
            })
        }),
        text: Text({
            color: '#fff',
            font: '14px sans-serif',
            text: size.toString(),
            fill: Fill({
                color: '#fff'
            })
        })
    })
}
// 创建MarkerStyle
const createMarkerStyle = (size) => {
    return Style({
        image: Icon({
            anchor: [0.5, 0.5],
            color: 'red',
            src: './img/point.png',
            width: 50
        })
    })
}
// 创建Vector图层
const vectorLayer = new ol.layer.Vector({
    source: Cluster,
    style: (feature) => {
        const len = feature.get('features').length;
        return len > 1 ? createClusterStyle(len) : createMarkerStyle(len);
    }
})

分别创建ClusterStyle 和 MarkerStyle,然后通过Vector图层的style函数进行判断,聚合主干的基本逻辑就完成了。
这里我们其实会发现,点聚合主要是数据源的聚合,配合ol.source.Cluster即可完成以上聚合的主要逻辑。

示例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>点聚合 Map</title>
    <link rel="stylesheet" href="./8.2.0/theme/ol.css">
    <script src="./8.2.0/en/latest/ol/dist/ol.js"></script>
    <!-- <script src="https://cdn.jsdelivr.net/npm/ol@v8.2.0/dist/ol.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v8.2.0/ol.css"> -->
</head>

<body>
    <div id="map" style="height: 95vh; border: 6px solid;"></div>

    <script>
        let map = new ol.Map({
            target: 'map',
            layers: [
                new ol.layer.Tile({
                    // source: new ol.source.OSM()
                    source: new ol.source.BingMaps({
                        key: 'Aud1dM5V1tT5Yvg2uX8Hp5ftaZZ4stEuJZ8i7IO1KSyLFj9A4UuamHK6_tUKEcGe',
                        imagerySet: 'Road'  // 指定加载的图层名,还可以替换为'AerialWithLabels' 'Aerial'或'Road'
                    })
                }),
            ],
            view: new ol.View({
                center: [116.39, 39.91],
                zoom: 11,
                projection: 'EPSG:4326'
            })
        });
        // 地图点击事件
        map.on('click', (e) => {
            console.log(e);
        })

        // 矢量数据
        const source = new ol.source.Vector();
        for (let i = 0; i < 200; i++) {
            const coordinate = [116.39 + Math.random(), 39.91 + Math.random()];
            const Feature = new ol.Feature(new ol.geom.Point(coordinate));
            source.addFeature(Feature);
        }
        for (let i = 0; i < 200; i++) {
            const coordinate = [116.40 + Math.random(), 39.92 + Math.random()];
            const Feature = new ol.Feature(new ol.geom.Point(coordinate));
            source.addFeature(Feature);
        }
        // 聚合数据
        const Cluster = new ol.source.Cluster({
            source: source,
            distance: 100
        })

        const Style = (options) => new ol.style.Style(options);
        const Fill = (options) => new ol.style.Fill(options);
        const Circle = (options) => new ol.style.Circle(options);
        const Text = (options) => new ol.style.Text(options);
        const Stroke = (options) => new ol.style.Stroke(options);
        const Icon = (options) => new ol.style.Icon(options);
        // 创建聚合Style
        const createClusterStyle = (size) => {
            console.log(size);
            return Style({
                image: Circle({
                    radius: 30,
                    stroke: Stroke({
                        color: '#fff'
                    }),
                    fill: Fill({
                        color: 'rgba(0,0,0, 0.7)'
                    })
                }),
                text: Text({
                    color: '#fff',
                    font: '14px sans-serif',
                    text: size.toString(),
                    fill: Fill({
                        color: '#fff'
                    })
                })
            })
        }
        // 创建Marker Style
        const createMarkerStyle = (size) => {
            return Style({
                image: Icon({
                    anchor: [0.5, 0.5],
                    color: 'red',
                    src: './img/point.png',
                    width: 50
                })
            })
        }
        // 创建Vector图层
        const vectorLayer = new ol.layer.Vector({
            source: Cluster,
            style: (feature) => {
                const len = feature.get('features').length;
                return len > 1 ? createClusterStyle(len) : createMarkerStyle(len);
            }
        })
        map.addLayer(vectorLayer);
        // 点标注点击
        map.on('click', (evt) => {
            const Feature = map.forEachFeatureAtPixel(evt.pixel, (feature) => {
                return feature
            })
            if (Feature && Feature.get('name') === 'Marker') {
                console.log('Marker点标注');
            }
        })
    </script>
</body>

</html>

image.png

总结

  • FeatureGeometryStyle三者结合,实现矢量图形的上图;
  • Geometry是feature对象的基本组成部分,存储一个要素的几何信息;
  • Feature对象会被挂载到矢量数据源的要素上,再上图;
  • StyleFeature的样式;
  • 点聚合实际是数据源的聚合;

HerryLo
238 声望11 粉丝

喜爱JavaScript和NodeJs;