GIS

OpenLayers Feature是什么

了解OpenLayers中Feature的基本概念、Geometry与属性、Feature创建与修改、样式设置、事件处理以及Feature与Vector Layer之间的关系。

阅读约 190 分钟

OpenLayers Feature是什么

在OpenLayers中,Feature(要素)是用于表示一个具有空间几何和属性信息的地图对象

例如:

  • 一个城市
  • 一条道路
  • 一个建筑物
  • 一个商店
  • 一个行政区域
  • 一个设备
  • 一个轨迹

都可以在OpenLayers中表示为Feature。

简单来说:

Feature = Geometry + Attributes

例如:

Feature
├── Geometry
│   └── Point
│       └── 坐标
│
└── Properties
    ├── name
    ├── type
    └── status

Feature是OpenLayers矢量数据开发中非常核心的对象。

Feature的基本结构

一个Feature通常包含:

Feature
├── Geometry
├── Properties
├── Style
└── ID

其中:

Geometry
    ↓
描述空间位置和形状

Properties
    ↓
描述业务属性

Style
    ↓
控制地图显示效果

ID
    ↓
标识Feature

例如一个商店Feature:

Feature
├── Geometry
│   └── Point
│       └── [116.397, 39.908]
│
├── Properties
│   ├── name = 商店A
│   ├── type = shop
│   └── status = open
│
└── Style
    └── 图标

创建Feature

OpenLayers可以通过Feature创建一个要素。

import Feature from 'ol/Feature.js'
import Point from 'ol/geom/Point.js'

const feature = new Feature({
  geometry: new Point([116.397, 39.908])
})

这样就创建了一个Point Feature。

结构:

Feature
    ↓
Point
    ↓
[116.397, 39.908]

Feature的Geometry

Feature最重要的组成部分之一就是Geometry。

例如:

const feature = new Feature({
  geometry: new Point([116.397, 39.908])
})

这里:

Feature
    ↓
geometry
    ↓
Point

Feature本身负责描述一个完整地图对象,而Geometry负责描述这个对象的空间形状。

Point Feature

Point是最常见的Feature类型之一。

import Feature from 'ol/Feature.js'
import Point from 'ol/geom/Point.js'

const feature = new Feature({
  geometry: new Point([116.397, 39.908])
})

可以表示:

商店
医院
学校
摄像头
充电桩
设备

例如:

Feature
└── Point
    └── [116.397, 39.908]

LineString Feature

LineString可以表示线状对象。

import LineString from 'ol/geom/LineString.js'

const feature = new Feature({
  geometry: new LineString([
    [116.397, 39.908],
    [116.405, 39.915],
    [116.412, 39.920]
  ])
})

可以表示:

道路
河流
铁路
管线
GPS轨迹

结构:

Feature
└── LineString
    ├── Point
    ├── Point
    └── Point

Polygon Feature

Polygon用于表示区域。

import Polygon from 'ol/geom/Polygon.js'

const feature = new Feature({
  geometry: new Polygon([
    [
      [116.390, 39.900],
      [116.400, 39.900],
      [116.400, 39.910],
      [116.390, 39.910],
      [116.390, 39.900]
    ]
  ])
})

可以表示:

行政区
建筑物
地块
公园
湖泊
规划区域

MultiGeometry

Feature的Geometry并不一定只能是单个对象。

也可以使用:

MultiPoint
MultiLineString
MultiPolygon
GeometryCollection

例如:

import MultiPoint from 'ol/geom/MultiPoint.js'

const feature = new Feature({
  geometry: new MultiPoint([
    [116.397, 39.908],
    [116.405, 39.915],
    [116.412, 39.920]
  ])
})

因此:

Feature
    ↓
Geometry
    ├── Point
    ├── LineString
    ├── Polygon
    ├── MultiPoint
    ├── MultiLineString
    └── MultiPolygon

Feature属性

Feature除了Geometry之外,还可以保存业务属性。

例如:

const feature = new Feature({
  geometry: new Point([116.397, 39.908]),
  name: '商店A',
  type: 'shop',
  status: 'open'
})

这些属性可以用于:

查询
筛选
弹窗
标注
样式
业务逻辑

例如:

feature.get('name')

得到:

商店A

get属性

可以使用get()读取Feature属性。

const name = feature.get('name')
const type = feature.get('type')
const status = feature.get('status')

例如:

console.log(feature.get('name'))

输出:

商店A

set属性

可以使用set()修改属性。

feature.set('status', 'closed')

也可以:

feature.set('name', '新商店')

修改后:

name
 ↓
新商店

getProperties

可以获取Feature的全部属性。

const properties = feature.getProperties()

例如:

{
  geometry: ...,
  name: '商店A',
  type: 'shop',
  status: 'open'
}

需要注意,Geometry本身也属于Feature对象的一个特殊属性。

Geometry的获取

可以使用:

const geometry = feature.getGeometry()

例如:

const geometry = feature.getGeometry()

console.log(geometry)

返回:

Point
LineString
Polygon

等Geometry对象。

修改Geometry

可以通过:

feature.setGeometry(newGeometry)

修改Feature的Geometry。

例如:

feature.setGeometry(
  new Point([116.410, 39.920])
)

原来的位置:

[116.397, 39.908]

变成:

[116.410, 39.920]

因此Feature可以动态移动。

Feature与Geometry的关系

需要特别理解:

Feature
    ↓
Geometry

Geometry负责:

位置
形状
坐标

Feature负责:

Geometry
+
业务属性
+
样式
+
标识

因此:

Geometry
    ↓
空间对象

Feature
    ↓
完整地图要素

Feature与VectorSource

Feature通常不会直接添加到Map。

典型结构是:

Map
 ↓
VectorLayer
 ↓
VectorSource
 ↓
Feature
 ↓
Geometry

例如:

import VectorSource from 'ol/source/Vector.js'

const source = new VectorSource({
  features: [feature]
})

这里:

VectorSource
    ↓
管理Feature集合

Feature添加到VectorSource

也可以动态添加:

source.addFeature(feature)

多个Feature:

source.addFeatures([
  feature1,
  feature2,
  feature3
])

结构:

VectorSource
├── Feature 1
├── Feature 2
└── Feature 3

Feature删除

删除一个Feature:

source.removeFeature(feature)

删除全部Feature:

source.clear()

例如:

source.clear()

可以清空当前VectorSource中的所有Feature。

VectorLayer

VectorLayer负责将VectorSource中的Feature显示到地图上。

import VectorLayer from 'ol/layer/Vector.js'

const layer = new VectorLayer({
  source
})

完整关系:

Map
 ↓
VectorLayer
 ↓
VectorSource
 ↓
Feature
 ↓
Geometry

这是OpenLayers矢量图层最重要的结构之一。

创建完整Feature

例如创建一个商店:

const feature = new Feature({
  geometry: new Point([116.397, 39.908]),
  name: '商店A',
  type: 'shop',
  status: 'open'
})

创建Source:

const source = new VectorSource({
  features: [feature]
})

创建Layer:

const layer = new VectorLayer({
  source
})

最后添加到Map:

map.addLayer(layer)

整体结构:

Map
 ↓
VectorLayer
 ↓
VectorSource
 ↓
Feature
 ├── Geometry
 │   └── Point
 │
 └── Properties
     ├── name
     ├── type
     └── status

Feature样式

Feature可以设置自己的Style。

例如:

import Style from 'ol/style/Style.js'
import CircleStyle from 'ol/style/Circle.js'
import Fill from 'ol/style/Fill.js'
import Stroke from 'ol/style/Stroke.js'

feature.setStyle(
  new Style({
    image: new CircleStyle({
      radius: 6,
      fill: new Fill({
        color: '#1677ff'
      }),
      stroke: new Stroke({
        color: '#ffffff',
        width: 2
      })
    })
  })
)

这样Point Feature就可以显示为一个圆形标记。

Feature Style与Layer Style

OpenLayers中既可以给Feature设置Style,也可以给Layer设置Style。

Feature Style:

feature.setStyle(style)

Layer Style:

const layer = new VectorLayer({
  source,
  style
})

可以理解为:

Feature Style
    ↓
控制单个Feature

Layer Style
    ↓
控制整个图层中的Feature

根据属性设置样式

实际项目中经常根据Feature属性动态设置Style。

例如:

const layer = new VectorLayer({
  source,
  style: (feature) => {
    const status = feature.get('status')

    if (status === 'warning') {
      return warningStyle
    }

    return normalStyle
  }
})

这样可以实现:

status = normal
    ↓
普通样式

status = warning
    ↓
警告样式

常用于:

设备监控
车辆监控
告警地图
人口地图
业务专题图

Feature事件

Feature支持属性变化等事件。

例如:

feature.on('change', () => {
  console.log('Feature发生变化')
})

当Feature发生变化时,可以进行相关处理。

Geometry变化事件

如果需要监听Geometry变化,可以:

const geometry = feature.getGeometry()

geometry.on('change', () => {
  console.log('Geometry发生变化')
})

例如用户拖动Feature时:

用户拖动
 ↓
Geometry坐标改变
 ↓
Geometry change
 ↓
更新业务数据

Feature ID

Feature可以设置唯一ID。

feature.setId('shop-001')

获取:

feature.getId()

返回:

shop-001

ID常用于:

数据更新
数据删除
Feature定位
业务对象关联

例如:

Feature
id = device-001

可以对应后端数据库:

device_id = device-001

Feature与后端数据

实际项目中,Feature通常来自后端GIS数据。

例如后端返回:

{
  "id": 1,
  "name": "商店A",
  "type": "shop",
  "geometry": {
    "type": "Point",
    "coordinates": [116.397, 39.908]
  }
}

前端转换后:

Backend Data
    ↓
GeoJSON
    ↓
OpenLayers
    ↓
Feature

GeoJSON转换为Feature

OpenLayers提供GeoJSON格式转换能力。

import GeoJSON from 'ol/format/GeoJSON.js'

const format = new GeoJSON()

const features = format.readFeatures(geojson)

这样可以将GeoJSON转换成OpenLayers Feature。

结构:

GeoJSON
    ↓
readFeatures()
    ↓
Feature[]

Feature转换为GeoJSON

也可以将Feature转换为GeoJSON。

const geojson = format.writeFeature(feature)

多个Feature:

const geojson = format.writeFeatures(features)

因此:

GeoJSON
 ↓
Feature

Feature
 ↓
GeoJSON

这对于前后端GIS数据交换非常重要。

Feature与WKT

如果后端使用WKT:

POINT (116.397 39.908)

可以先通过WKT格式解析器转换成Geometry或Feature。

典型流程:

WKT
 ↓
OpenLayers Format
 ↓
Feature
 ↓
VectorSource
 ↓
VectorLayer
 ↓
Map

因此Feature是各种GIS数据格式进入OpenLayers地图渲染体系的重要中间对象。

Feature选择

OpenLayers可以对地图上的Feature进行选择。

例如:

import Select from 'ol/interaction/Select.js'

const select = new Select()

map.addInteraction(select)

用户点击Feature后:

用户点击
 ↓
Select
 ↓
Feature
 ↓
selected Feature

可以获取:

select.getFeatures()

Feature点击事件

可以监听Select:

select.on('select', (event) => {
  const feature = event.selected[0]

  if (feature) {
    console.log(feature.get('name'))
  }
})

例如:

点击地图对象
    ↓
获取Feature
    ↓
读取name
    ↓
显示弹窗

Feature弹窗

一个常见的WebGIS流程:

用户点击Feature
        ↓
获取Feature
        ↓
feature.getProperties()
        ↓
业务属性
        ↓
Overlay
        ↓
Popup

例如:

select.on('select', (event) => {
  const feature = event.selected[0]

  if (!feature) {
    return
  }

  const name = feature.get('name')

  console.log(name)
})

然后可以将数据放到Popup中。

Feature拖动

OpenLayers可以通过交互实现Feature拖动。

例如:

import Translate from 'ol/interaction/Translate.js'

const translate = new Translate({
  features: select.getFeatures()
})

map.addInteraction(translate)

拖动完成后可以获取新的Geometry:

translate.on('translateend', (event) => {
  event.features.forEach((feature) => {
    const geometry = feature.getGeometry()

    console.log(geometry)
  })
})

典型流程:

用户拖动
 ↓
Feature
 ↓
Geometry坐标改变
 ↓
translateend
 ↓
保存新坐标

Feature编辑

Feature编辑是WebGIS中非常常见的功能。

典型工具:

Draw
Modify
Translate
Snap
Select

流程:

用户选择Feature
      ↓
Modify
      ↓
修改Geometry
      ↓
获取新Geometry
      ↓
提交后端

Feature与Draw

Draw可以创建新的Feature。

例如:

import Draw from 'ol/interaction/Draw.js'

const draw = new Draw({
  source,
  type: 'Point'
})

map.addInteraction(draw)

用户点击地图:

用户点击
 ↓
Draw
 ↓
创建Geometry
 ↓
创建Feature
 ↓
VectorSource

监听:

draw.on('drawend', (event) => {
  const feature = event.feature

  console.log(feature)
})

Feature与Modify

Modify用于修改Feature的Geometry。

import Modify from 'ol/interaction/Modify.js'

const modify = new Modify({
  source
})

map.addInteraction(modify)

例如修改Polygon:

Polygon Feature
      ↓
Modify
      ↓
拖动顶点
      ↓
Geometry改变
      ↓
新的Polygon

Feature与Snap

编辑线和面时,经常需要吸附。

import Snap from 'ol/interaction/Snap.js'

const snap = new Snap({
  source
})

map.addInteraction(snap)

可以实现:

顶点
 ↓
吸附
 ↓
已有Geometry顶点

这对于:

地籍编辑
道路编辑
规划数据编辑
管网编辑

非常重要。

Feature与空间数据

Feature可以看作OpenLayers中的业务空间对象。

例如:

城市
 ↓
Feature

道路
 ↓
Feature

建筑
 ↓
Feature

设备
 ↓
Feature

每个Feature通常包含:

Geometry
+
Properties
+
Style
+
ID

Feature与Layer的关系

Feature不是Layer。

两者职责不同:

Feature
 ↓
一个地图对象

VectorSource
 ↓
管理多个Feature

VectorLayer
 ↓
显示一组Feature

Map
 ↓
显示多个Layer

完整结构:

Map
├── VectorLayer
│   └── VectorSource
│       ├── Feature
│       ├── Feature
│       └── Feature
│
└── TileLayer
    └── TileSource

Feature与VectorSource的区别

Feature:

一个对象

VectorSource:

一组Feature的容器

例如:

VectorSource
├── 商店A
├── 商店B
├── 商店C
└── 医院A

这些对象都是Feature。

Feature与GeoJSON Feature的区别

这两个概念名称相同,但不是完全相同的东西。

GeoJSON Feature:

{
  "type": "Feature",
  "geometry": {},
  "properties": {}
}

OpenLayers Feature:

ol/Feature

它是OpenLayers中的JavaScript对象。

可以理解为:

GeoJSON Feature
    ↓
数据格式中的Feature

OpenLayers Feature
    ↓
OpenLayers运行时对象

OpenLayers可以将GeoJSON Feature转换成自己的Feature对象。

Feature与Geometry的核心区别

可以记住:

Geometry
 ↓
只关心空间形状

Feature
 ↓
Geometry
+
属性
+
业务对象

例如:

Point
[116.397, 39.908]

只是Geometry。

而:

商店A
位置:[116.397, 39.908]
类型:shop
状态:open

就是一个完整的Feature。

Feature在WebGIS中的位置

OpenLayers的核心数据结构可以理解为:

Map
 ↓
Layer
 ↓
Source
 ↓
Feature
 ↓
Geometry
 ↓
Coordinates

对于矢量数据:

Map
 ↓
VectorLayer
 ↓
VectorSource
 ↓
Feature
 ↓
Geometry

这条关系非常重要。

Feature完整生命周期

一个Feature通常经历:

创建
 ↓
设置Geometry
 ↓
设置Properties
 ↓
加入VectorSource
 ↓
显示在VectorLayer
 ↓
用户选择
 ↓
修改Geometry
 ↓
修改Properties
 ↓
保存到后端
 ↓
更新或删除

可以理解为:

数据
 ↓
Feature
 ↓
地图
 ↓
交互
 ↓
编辑
 ↓
保存

Feature常见操作

操作OpenLayers方法
获取GeometrygetGeometry()
设置GeometrysetGeometry()
获取属性get()
设置属性set()
获取全部属性getProperties()
设置IDsetId()
获取IDgetId()
设置StylesetStyle()
添加到Sourcesource.addFeature()
删除Featuresource.removeFeature()

Feature常见应用

Feature可以用于构建:

POI地图
设备地图
车辆监控
轨迹地图
电子围栏
行政区地图
管网地图
地籍地图
规划地图
物流地图

例如智慧工厂:

设备
 ↓
Feature
 ↓
Point / Polygon
 ↓
设备状态
 ↓
动态Style
 ↓
地图

Feature与业务状态

实际项目中可以将业务状态保存在Feature属性中。

例如:

const feature = new Feature({
  geometry: new Point([116.397, 39.908]),
  deviceId: 'device-001',
  status: 'warning',
  name: '设备A'
})

然后根据:

feature.get('status')

决定显示样式。

例如:

normal
 ↓
正常样式

warning
 ↓
警告样式

offline
 ↓
离线样式

这是一种非常常见的WebGIS开发方式。

Feature数据更新

例如后端实时返回设备位置:

feature.setGeometry(
  new Point([116.410, 39.920])
)

更新状态:

feature.set('status', 'online')

更新名称:

feature.set('name', '设备B')

这样就可以实现地图对象实时更新。

Feature与实时地图

实时GIS系统通常采用:

后端WebSocket
       ↓
业务数据
       ↓
找到Feature
       ↓
setGeometry()
       ↓
set()
       ↓
地图自动更新

例如车辆监控:

车辆GPS
 ↓
WebSocket
 ↓
vehicleId
 ↓
对应Feature
 ↓
更新Geometry
 ↓
车辆在地图上移动

Feature的核心关系

可以用下面的结构理解OpenLayers Feature:

OpenLayers
│
├── Map
│
├── Layer
│   └── VectorLayer
│
├── Source
│   └── VectorSource
│
├── Feature
│   ├── Geometry
│   │   ├── Point
│   │   ├── LineString
│   │   └── Polygon
│   │
│   ├── Properties
│   ├── Style
│   └── ID
│
└── Interaction
    ├── Select
    ├── Draw
    ├── Modify
    ├── Translate
    └── Snap

OpenLayers Feature完整流程

可以记住:

数据
 ↓
GeoJSON / WKT / 后端API
 ↓
OpenLayers Format
 ↓
Feature
 ↓
Geometry + Properties
 ↓
VectorSource
 ↓
VectorLayer
 ↓
Map

用户进行操作:

Map
 ↓
Select / Draw / Modify / Translate
 ↓
Feature
 ↓
Geometry变化
 ↓
业务数据变化
 ↓
保存后端

总结

OpenLayers中的Feature是表示地图矢量对象的核心对象。

一个Feature通常包含:

Geometry
Properties
Style
ID

其中:

Geometry负责描述空间位置和形状,Properties负责描述业务属性,Feature则将空间信息与业务信息组织成一个完整的地图对象。

OpenLayers中常见结构是:

Map
 ↓
VectorLayer
 ↓
VectorSource
 ↓
Feature
 ↓
Geometry

常见Geometry包括:

Point
LineString
Polygon
MultiPoint
MultiLineString
MultiPolygon

Feature还可以配合:

Select
Draw
Modify
Translate
Snap

实现地图选择、绘制、编辑、拖动和吸附。

因此可以记住:

VectorSource管理Feature,VectorLayer显示Feature,Feature管理Geometry和属性,而Interaction负责让用户与Feature进行交互。

🧰

相关工具

使用 IYATools 在线工具快速处理 GIS 数据