1. 地图绑定数据
本章节内,我们将创建:
-
LocationType枚举。 -
带
GeoPoint类型属性的Location实体。 -
CRUD 视图,其中
Location.detail视图中使用了地图。
创建 Location 实体和视图
首先,我们生成一个 LocationType 枚举,包含两个值:Office 和 Coworking。创建枚举的详细说明请参阅:使用枚举。
下一步,创建 Location 实体。创建实体的详细说明请参阅:简单增删改查。
在 New JPA Entity 对话框中勾选 Traits → Versioned 复选框。
Location 实体的属性如下:
-
city-String类型。勾选 Mandatory 复选框。 -
address-String类型。勾选 Mandatory 复选框。 -
type-LocationType枚举类型。 -
building-GeoPoint类型。勾选 Mandatory 复选框:
使用 address 属性定义实体的 实例名。
为 Location 实体创建 CRUD 视图。创建实体 CRUD 视图的详细说明请参阅:创建 CRUD 视图。
在创建视图的过程中,每一步都使用推荐的配置即可。
Studio 会自动生成两个视图:Location.list 和 Location.detail。
在视图中添加地图
从 Jmix 工具窗口找到 location-detail-view.xml 文件并双击打开视图设计器:
可以看到 building 属性使用的是 textField 组件。
如需在视图中展示地图,XML 里需要包含 geoMap 组件。
将光标定位在 formLayout 元素之后。
点击操作面板的 Add Component,然后找到 GeoMap 并双击。
此时,Jmix UI 层级结构和 XML 中都会在 formLayout 元素的下方添加新的 geoMap 元素。按照下面的代码配置 id、height 和 width 属性。
<maps:geoMap id="map" height="100%" width="100%"/>
添加 OsmSource 的瓦片层
我们将使用一个栅格层(Raster layer)作为地图的基础背景层。这里,我们使用 OsmSource,这是一个预定义用于展示 OpenStreetMap 瓦片的源。
在 Jmix UI 结构面板或 XML 中选择 map,然后点击组件面板的 Add 按钮。在下拉列表中,选择 Layers → TileLayer。
在 Jmix UI 结构面板或 XML 中选择 maps:tile,然后点击组件面板的 Add 按钮。在下拉列表中,选择 OsmSource。
现在我们可以启动应用程序查看新添加的地图。
点击主工具栏的 Debug 按钮()。
在应用程序启动之前,Studio 会比较项目中的数据模型和数据库的表结构。当存在新建实体时,Studio 会自动生成修改数据库的 Liquibase changelog(例如,创建 LOCATION 表)。
点击 Save and run。
Studio 会先在数据库运行 changelog,然后构建并启动应用程序:
应用程序启动完成后,可以在浏览器打开 http://localhost:8080 用凭证 admin/admin 登录。
从 Application 菜单中选择 Locations 打开 Location.list 视图。点击 Create 打开 Location.detail 视图:
地图配置
默认情况下,geoMap 组件展示初始位置为 (0,0) 的世界地图。
我们将使用 mapView 配置地图展示的位置。
在 Jmix UI 结构面板或 XML 中选择 map,然后点击组件面板的 Add 按钮。在下拉列表中,选择 MapView。
设置 centerY 属性:
<maps:mapView centerY="51.0"/>
在 Jmix UI 结构面板或 XML 中选择 maps:mapView,然后点击组件面板的 Add 按钮。在下拉列表中,选择 Extent,然后按照下面的代码配置 minX、minY、maxX 和 maxY 属性。
<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 的处理方法:
在 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 获取 GeometryFactory。GeometryUtils 是 JTS 库相关的一个工具类。 |
| 2 | 通过 GeometryFactory 的方法将从 event 中获取的坐标转换为 Point 对象。 |
| 3 | 使用基类 StandardDetailView 的 getEditedEntity() 方法获取正在编辑的 Location 实体。 |
启动应用程序,从 Application 菜单中选择 Locations 打开 Location.list 视图。点击 Create 打开 Location.detail 视图。填写 City、Address 和 Type 的信息。在地图上点击特定的地址,Building 字段会自动填入地图上选取的坐标。点击 OK 保存数据。