1. 地图绑定数据

本章节内,我们将创建:

  • LocationType 枚举。

  • GeoPoint 类型属性的 Location 实体。

  • CRUD 视图,其中 Location.detail 视图中使用了地图。

创建 Location 实体和视图

首先,我们生成一个 LocationType 枚举,包含两个值:OfficeCoworking。创建枚举的详细说明请参阅:使用枚举

enumeration location type

下一步,创建 Location 实体。创建实体的详细说明请参阅:简单增删改查

New JPA Entity 对话框中勾选 Traits → Versioned 复选框。

new location entity dialog

Location 实体的属性如下:

  • city - String 类型。勾选 Mandatory 复选框。

  • address - String 类型。勾选 Mandatory 复选框。

  • type - LocationType 枚举类型。

  • building - GeoPoint 类型。勾选 Mandatory 复选框:

    geo point attribute

使用 address 属性定义实体的 实例名

Location 实体创建 CRUD 视图。创建实体 CRUD 视图的详细说明请参阅:创建 CRUD 视图

在创建视图的过程中,每一步都使用推荐的配置即可。

Studio 会自动生成两个视图:Location.listLocation.detail

在视图中添加地图

Jmix 工具窗口找到 location-detail-view.xml 文件并双击打开视图设计器:

location detail view

可以看到 building 属性使用的是 textField 组件。

如需在视图中展示地图,XML 里需要包含 geoMap 组件。

将光标定位在 formLayout 元素之后。

点击操作面板的 Add Component,然后找到 GeoMap 并双击。

adding map

此时,Jmix UI 层级结构和 XML 中都会在 formLayout 元素的下方添加新的 geoMap 元素。按照下面的代码配置 idheightwidth 属性。

<maps:geoMap id="map" height="100%" width="100%"/>

添加 OsmSource 的瓦片层

我们将使用一个栅格层(Raster layer)作为地图的基础背景层。这里,我们使用 OsmSource,这是一个预定义用于展示 OpenStreetMap 瓦片的源。

Jmix UI 结构面板或 XML 中选择 map,然后点击组件面板的 Add 按钮。在下拉列表中,选择 Layers → TileLayer

add tile layer

Jmix UI 结构面板或 XML 中选择 maps:tile,然后点击组件面板的 Add 按钮。在下拉列表中,选择 OsmSource

add osmsource

现在我们可以启动应用程序查看新添加的地图。

点击主工具栏的 Debug 按钮(start debugger)。

在应用程序启动之前,Studio 会比较项目中的数据模型和数据库的表结构。当存在新建实体时,Studio 会自动生成修改数据库的 Liquibase changelog(例如,创建 LOCATION 表)。

changelog location

点击 Save and run

Studio 会先在数据库运行 changelog,然后构建并启动应用程序:

run app

应用程序启动完成后,可以在浏览器打开 http://localhost:8080 用凭证 admin/admin 登录。

Application 菜单中选择 Locations 打开 Location.list 视图。点击 Create 打开 Location.detail 视图:

location detail1

地图配置

默认情况下,geoMap 组件展示初始位置为 (0,0) 的世界地图。

我们将使用 mapView 配置地图展示的位置。

Jmix UI 结构面板或 XML 中选择 map,然后点击组件面板的 Add 按钮。在下拉列表中,选择 MapView

add map view

设置 centerY 属性:

<maps:mapView centerY="51.0"/>

Jmix UI 结构面板或 XML 中选择 maps:mapView,然后点击组件面板的 Add 按钮。在下拉列表中,选择 Extent,然后按照下面的代码配置 minXminYmaxXmaxY 属性。

<maps:extent minX="-15.0"
             minY="30.0"
             maxX="40.0"
             maxY="60.0"/>

Extent(范围)配置的是地图的可见区域,也就是说,超出可见区域外的部分将不显示。

启动应用程序以查看最新改动。

添加 DataVectorSource 的矢量层

为了处理地理对象,我们将引入一个矢量层。

Jmix UI 结构面板或 XML 中选择 maps:layers,然后点击组件面板的 Add 按钮。在下拉列表中,选择 VectorLayer。设置其 id 为:id="vectorLayer"

我们使用 DataVectorSource,这个源支持与 Jmix 数据容器进行数据绑定。

Jmix UI 结构面板或 XML 中选择 vectorLayer,然后点击组件面板的 Add 按钮。在下拉列表中,选择 DataVectorSource。设置其 dataContainer 属性:

<maps:vector id="vectorLayer">
    <maps:dataVectorSource id="dataVectorSource"
                           dataContainer="locationDc"
                           property="building"/>
</maps:vector>

现在地图的完整配置如下:

<maps:geoMap id="map" height="100%" width="100%">
    <maps:layers>
        <maps:tile>
            <maps:osmSource/>
        </maps:tile>
        <maps:vector id="vectorLayer">
            <maps:dataVectorSource id="dataVectorSource"
                                   dataContainer="locationDc"
                                   property="building"/>
        </maps:vector>
    </maps:layers>
    <maps:mapView centerY="51.0">
        <maps:extent minX="-15.0"
                     minY="30.0"
                     maxX="40.0"
                     maxY="60.0"/>
    </maps:mapView>
</maps:geoMap>

在实体属性中保存坐标

当 HR 经理创建一个位置时,可以点击地图中的一个点,然后这个点的坐标会保存在 Location 实体的一个 Point 类型的属性中。

现在我们为地图添加一个 ClickEvent 事件。

Jmix UI 结构面板或 XML 中选择 map,切换至 Handlers tab,创建 MapClickEvent 的处理方法:

add click event

map 点击事件的处理方法中,添加获取并保存坐标的业务逻辑:

public class LocationDetailView extends StandardDetailView<Location> {
    protected GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory(); (1)

    @Subscribe("map")
    public void onMapMapClick(final MapClickEvent event) {
        Point point = geometryFactory.createPoint(event.getCoordinate()); (2)
        Location location = getEditedEntity(); (3)
        location.setBuilding(point);
    }
}
1 GeometryUtils 获取 GeometryFactoryGeometryUtils 是 JTS 库相关的一个工具类。
2 通过 GeometryFactory 的方法将从 event 中获取的坐标转换为 Point 对象。
3 使用基类 StandardDetailViewgetEditedEntity() 方法获取正在编辑的 Location 实体。

启动应用程序,从 Application 菜单中选择 Locations 打开 Location.list 视图。点击 Create 打开 Location.detail 视图。填写 CityAddressType 的信息。在地图上点击特定的地址,Building 字段会自动填入地图上选取的坐标。点击 OK 保存数据。

location detail2