矢量层

矢量层 VectorLayer 是用于在地图上显示实体的基础层。该图层是一个数据感知组件,将数据(几何对象)和地图连接起来。矢量图层支持在地图上进行简单显示、交互式编辑和绘制几何对象。

几何对象

几何对象是指具有几何属性的实体。此属性应具有包含在 io.jmix.maps.datatype 包中的特定几何数据类型之一:

数据类型

Java 类型

geoPoint

org.locationtech.jts.geom.Point

geoPolyline

org.locationtech.jts.geom.LineString

geoPolygon

org.locationtech.jts.geom.Polygon

通过实体设计器创建,添加一个新的属性并在下拉列表选择特定的几何类型。

geo types

打开实体源码,并添加下列注解:

  • @Geometry - 标记该属性用于在地图中展示几何对象。

    几何对象必须包含一个几何属性,否则在绘制图层时会抛出异常。
  • @Convert - 指定一个自定义的 JPA 转换器,定义这种数据类型如何进行持久化。

    默认情况下,扩展组件会使用 JPA 转换器将坐标转换为 WKT 格式,然后使用文本类型保存。从数据加载时,文本也会反向解析为对象。这些转换器位于 io.jmix.maps.converter.wkt 包中。

几何对象 Order 示例:

@JmixEntity
public class Order {
    //...
    @Geometry
    @PropertyDatatype("geoPoint")
    @Column(name = "LOCATION")
    @JmixProperty
    private Point location;
    //...
}

可以看到,Order 是一个很简单的实体,其中一个属性 locationorg.locationtech.jts.geom.Polygon 类型。

绑定几何对象至图层

通过 datacontainer 将几何对象绑定至图层。可以在 XML 中声明:

<maps:layers selectedLayer="orderLayer">
    <maps:tile id="tileLayer" tileProvider="map_OpenStreetMap"/>
    <maps:vector id="orderLayer" dataContainer="orderDc" editable="true"/>
</maps:layers>

iddataContainer 是必需参数。矢量层可以支持 InstanceContainerCollectionContainer 数据容器。

editable 参数定义图层是否可修改。

如需在矢量层编辑/绘制几何图形,需在 layersselectedLayer 参数指定相应的图层。

另外,还支持在界面控制器创建 VectorLayer

@Autowired
private GeoMap map;

@Autowired
private InstanceContainer<Order> orderDc;

@Subscribe
public void onBeforeShow(BeforeShowEvent event) {
    VectorLayer<Order> orderLayer = new VectorLayer<>("orderLayer", orderDc);
    orderLayer.setEditable(true);
    map.addLayer(orderLayer);
    map.selectLayer(orderLayer);
}

设置几何图形样式

使用 setStyleProvider() 方法确定几何对象的图形样式。也可以使用 @Install 注解在界面控制器中声明式的定义该方法,示例:

@Autowired
private GeometryStyles geometryStyles;

@Install(to = "map.territoryLayer", subject = "styleProvider")
private GeometryStyle mapTerritoryLayerStyleProvider(Territory territory) {
    return geometryStyles.polygon()
            .setFillColor("#08a343")
            .setStrokeColor("#004912")
            .setFillOpacity(0.3)
            .setStrokeWeight(1);
}

使用 io.jmix.mapsui.component.layer.style.GeometryStyles bean 为不同的几何图形类型创建样式。

处理选中的几何对象

几何对象可以通过用户点击或者通过关联的数据容器自动选中。如果 VectorLayer 设置为活动图层,则用户可以通过点击选取几何对象。

setSelectedGeoObject() 方法设置图层中选中的几何对象。例如,如果实体在编辑界面打开,则会自动在对应的矢量层中选中。

VectorLayer 订阅了数据容器变更事件,当数据容器中新增或删除数据时,会自动刷新。

选中几何对象时,会产生 GeoObjectSelectedEvent 事件。可以在界面控制器中订阅该事件,例如,在表格中选中几何对象:

@Autowired
private GroupTable<Order> ordersTable;

@Subscribe("map.orderLayer")
public void onMapOrderLayerGeoObjectSelected(VectorLayer.GeoObjectSelectedEvent<Order> event) {
    ordersTable.setSelected(event.getItem());
}

聚合

对于由几何点构成的矢量图层,可以将一些靠近的点划分为一个聚合点,在某些缩放级别将多个点显示为一个点:

maps clustering

如需使用聚合,需要在 XML 的 vector 元素内添加 cluster 元素:

<maps:layers>
    <maps:tile id="tiles" tileProvider="sample_CartoTileProvider"/>
    <maps:vector id="orders" dataContainer="ordersDc">
        <maps:cluster/>
    </maps:vector>
</maps:layers>

你还可以配置一些聚合的属性:

  • radius - 聚合能覆盖的最大半径,以像素为单位,默认 80

  • weightProperty - 如果指定,则图层中每个点都会有一个权重值(int),由几何对象的权重属性定义。该值用于计算聚合中点值的总和(默认情况下,总和为点的数量)。

  • showCoverage - 当鼠标悬停于聚合点时,显示其覆盖的范围。

  • disableAtZoom - 指定一个缩放级别,从该级别开始不显示聚合点。

  • showSinglePointAsCluster - 将单一点也显示为大小为 1 的聚合点。

几何对象底层的 Vaadin 组件

对地图中展示的每个几何对象,扩展组件都会创建一个 io.jmix.mapsui.component.leaflet.translators.GeoObjectWrapper 类的实例,其中包含底层的 Vaadin 组件。这个类提供与其包装的组件直接交互的方法:

  • openPopup() - 如果指定弹窗内容,则打开几何对象的弹窗。

  • closePopup() - 关闭几何对象的弹窗。

  • openTooltip() - 如果指定提示窗内容,则打开几何对象的提示窗。

  • closeTooltip() - 关闭几何对象的提示窗。

  • getLeafletComponent() - 返回客户端层展示的小树叶组件底层的 Vaadin 组件。

如需获取矢量层的几何对象包装器,可以调用 GeoMapImpl 类(实现了 GeoMap)的 getGeoObjectWrappersMap() 方法,并将矢量层传递给该方法:

VectorLayer<Order> ordersLayer = map.getLayer("orderLayer");
Map<?, GeoObjectWrapper<Order>> geoObjectWrappersMap =
        ((GeoMapImpl) map).getGeoObjectWrappersMap(ordersLayer);

返回的键值对中的 key 为几何对象的 ID(或几何对象本身,如果 ID = null),value 为对应的 GeoObjectWrapper 实例。因此,可以用下面的方式获取特定几何对象的 GeoObjectWrapper

@Autowired
private GroupTable<Order> ordersTable;

@Subscribe("map.orderLayer")
public void onMapOrderLayerGeoObjectSelected(VectorLayer.GeoObjectSelectedEvent<Order> event) {
    ordersTable.setSelected(event.getItem());
    GeoObjectWrapper<Order> geoObjectWrapper = geoObjectWrappersMap.get(event.getItem().getId());
    if (geoObjectWrapper != null) {
        geoObjectWrapper.openPopup();
    }
}
如果几何对象的几何值是 null,则该几何对象没有 GeoObjectWrapper

另外,当刷新图层时,GeoObjectWrapper 实例可能会发生改变或被替换。因此每次都需要用这个键值对获取相关的包装类实例。