
处理地理空间数据和地图是许多业务应用程序不可或缺的部分。 它可以是城市和区域信息系统,石油和天然气行业的应用程序,运输基础设施管理系统以及交付服务等。 在我们的CUBA平台中,除了开箱即用提供的基本功能之外,要构建此类应用程序,还提供了相当广泛的附加组件和组件 。 其中之一是Charts and Maps ,除了显示图表外,还可以将Google Maps集成到应用程序的可视部分。 去年,谷歌更新了其地图服务的使用条款,这导致成本增加,并且还引入了强制使用API的付款资料必须存在的条件。 这些情况导致大多数客户考虑使用替代卡提供商,因此系统提示我们开发新的卡组件。
现在,我们很高兴地介绍一个全新的组件-CUBA Maps 。 CUBA Maps以可视化表示和直观的工具来补充应用程序功能,以编辑地理空间数据。 该组件可同时处理栅格数据和矢量数据。 您可以使用任何与Web Map Service协议兼容的地图提供程序,也可以使用XYZ格式的图块作为栅格数据。 为了处理矢量数据,该组件使用来自JTS拓扑套件(JTS)库的几何数据类型(点,折线,多边形)-最流行的Java库,用于处理地理空间数据。 该组件提供了用于创建基于CUBA的全面地理信息系统的所有必要工具。
在本文中,我们将讨论Maps组件提供的新功能,并将其与我们以前的map组件进行比较。
基于层的结构
该组件支持在专业地理信息系统中广泛使用的传统多层结构。 图层主要分为栅格和矢量。 栅格图层由栅格图像组成,而矢量图层包含矢量几何。
该组件支持以下类型的层:
- 切片层以XYZ格式显示切片服务提供的切片。
- Web地图服务(WMS)层显示WMS服务提供的图像。
- 向量层包含地理对象(具有几何属性的实体)。
这些图层是地图的结构元素。 例如,下层可以是由图块组成的底图,第二层可以包含描述领土单位(例如区域)的多边形,而上层可以包含地理位置(客户,商店等的位置)。 将这些图层叠加在一起,我们得到最终的地图:

通过这种方法,您可以创建包含任何内容的结构清晰的地图。
CUBA Maps提供了一个新的可视组件GeoMap
。 在组件的XML描述符中,您可以设置地图的基本参数以及一组显示的图层。 这种配置的示例:
<maps:geoMap id="map" height="600px" width="100%" center="37.615, 55.752" zoom="10"> <maps:layers selectedLayer="addressLayer"> <maps:tile id="tiles" tileProvider="maps_OpenStreetMap"/> <maps:vector id="territoryLayer" dataContainer="territoryDc"/> <maps:vector id="addressLayer" dataContainer="addressDc" editable="true"/> </maps:layers> </maps:geoMap>
这种方法可让您获得图表和地图所缺乏的更多灵活性:
- 分层。 这种结构允许您构建具有任何内容的卡,例如,结合各种服务提供的图块。
- 层提供了将同类对象聚集在一起的抽象。 在“ 图表和地图”组件中,所有地图内容(例如点,多边形等)都被转储到UI组件的公共堆中。 为了以某种方式构造这些对象,项目团队必须编写其他逻辑。
- 一种描述层的声明方法。 如上面的示例所示,您可以在XML描述符中完全指定地图结构(图层集)。 在许多情况下,这足以在屏幕控制器中不实现任何其他逻辑。 如果不使用其他逻辑,使用图表和地图几乎是不可能的。
使用图块图层或WMS,可以与任何首选的地图提供程序一起使用。 您不受限于特定提供者,就像在Charts和Maps中那样 。
矢量图层极大地简化了地理对象在地图上的显示,交互式编辑和绘制。
还要注意的是,默认情况下, GeoMap
的可视组件具有特殊的辅助层Canvas 。 Canvas提供了一个方便的API,用于在地图上显示和绘制几何图形(点,折线,多边形)。 我们将在本文后面介绍使用Canvas的示例。
地理对象
假设我们有一个实体,其中包含与几何相关的属性(点,折线,多边形)。 我们将这个实体称为geo-object 。 因此,该组件极大地简化了地理对象的工作。
例如,考虑地理对象Address :
@Entity public class Address extends StandardEntity { ... @Column(name = "LOCATION") @Geometry @MetaProperty(datatype = "GeoPoint") @Convert(converter = CubaPointWKTConverter.class) protected Point location; ... }
它具有JTS拓扑套件(JTS)库中的org.locationtech.jts.geom.Point
类型的location
属性。 该组件支持JTS中的以下几何类型:
org.locationtech.jts.geom.Point
时间段。org.locationtech.jts.geom.LineString
折线。org.locationtech.jts.geom.Polygon
多边形。
用@Geometry
注释标记location
属性。 此注释宣布在地图上显示地理对象时应使用此属性的值。 该属性还标有以下注释:
@MetaProperty
在这种情况下,用于指示数据类型属性。 CUBA框架使用Datatype
接口在字符串之间来回转换值。@Convert
为持久属性定义JPA转换器。 JPA转换器在数据库和Java代码的表示形式之间转换属性值。 该组件提供了一组空间数据类型和JPA转换器。 组件文档中提供了更多信息。 您还可以使用自己的JPA转换器实现,这使得可以处理各种空间数据源(例如PostGIS )。
因此,为了将一个实体变成一个地理对象,您需要定义一个JTS几何类型的属性,并使用@Geometry
对其进行@Geometry
。 还有另一种选择-通过提供getter / setter方法来创建非持久属性。 如果您不想更改数据模型并重新生成DDL脚本,这将很有用。
例如,考虑具有单独的纬度和经度属性的实体地址 :
import com.haulmont.addon.maps.gis.utils.GeometryUtils; ... @Entity public class Address extends StandardEntity { ... @Column(name = "LATITUDE") protected Double latitude; @Column(name = "LONGITUDE") protected Double longitude; ... @Geometry @MetaProperty(datatype = "GeoPoint", related = {"latitude", "longitude"}) public Point getLocation() { if (getLatitude() == null || getLongitude() == null) { return null; } return GeometryUtils.createPoint(getLongitude(), getLatitude()); } @Geometry @MetaProperty(datatype = "GeoPoint") public void setLocation(Point point) { Point prevValue = getLocation(); if (point == null) { setLatitude(null); setLongitude(null); } else { setLatitude(point.getY()); setLongitude(point.getX()); } propertyChanged("location", prevValue, point); } ... }
如果决定使用此方法,请确保在setter中调用propertyChanged
方法,因为组件通过更新地图上的几何来响应此事件。
现在,我们已经准备好地理对象的类,可以将此类的实例添加到矢量层。 向量层本质上是数据(地理对象)和地图之间的连接元素。 要将地理对象与图层连接,您需要传输数据容器,或者在屏幕陈旧(CUBA的版本7以下)的情况下,将数据源传输到矢量层。 这可以在XML描述符中完成:
<maps:geoMap id="map"> <maps:layers> ... <maps:vector id="addressesLayer" dataContainer="addressesDc"/> </maps:layers> </maps:geoMap>
结果,包含在addressesDc
容器中的Address
类实例将显示在地图上。
考虑一下基本任务:创建一个用于编辑带有地图的地理对象的屏幕,您可以在其中编辑对象的几何形状。 要解决该问题,您需要在编辑屏幕的XML描述符中声明GeoMap的可视组件,并添加一个与包含已编辑地理对象的容器关联的矢量层:
<maps:geoMap id="map" height="600px" width="100%" center="37.615, 55.752" zoom="10"> <maps:layers selectedLayer="addressLayer"> <maps:tile ..."/> <maps:vector id="addressLayer" dataContainer="addressDc" editable="true"/> </maps:layers> </maps:geoMap>
如果将矢量图层标记为可编辑,则将激活对地图上地理对象的交互式编辑。 如果对象的几何形状为空,则地图将自动切换到绘图模式。 如您所见,要解决此问题,在地图上声明一个矢量层并将其传递给数据容器/数据源就足够了。
仅此而已。 如果我们使用“ 图表”和“地图”解决相同的问题,则必须在屏幕控制器中编写大量代码才能提供类似的功能。 使用Maps的新组件,解决这些问题变得更加简单。
帆布
有时您需要不使用实体。 相反,您想要一个简单的API在地图上添加和绘制几何图形,就像在Charts和Maps中一样 。 为此, GeoMap
视觉组件具有特殊的图层-Canvas 。 这是一个辅助层,默认情况下位于地图上,并提供用于在地图上添加和绘制几何图形的简单API。 您可以通过调用map.getCanvas()
方法map.getCanvas()
地图。
接下来,我们将看一些简单的任务,如何在“ 图表”和“地图”中解决它们,以及如何使用Canvas来完成这些任务。
在地图上显示几何
在“ 图表和地图”中,使用地图的可视组件创建几何对象,并用作工厂,然后将其添加到地图中:
Marker marker = map.createMarker(); GeoPoint position = map.createGeoPoint(lat, lon); marker.setPosition(position); map.addMarker(marker);
新的Maps组件可直接与JTS库中的类一起使用:
CanvasLayer canvasLayer = map.getCanvas(); Point point = address.getLocation(); canvasLayer.addPoint(point);
几何编辑
在“ 图表和地图”中,可以将几何指定为可编辑的。 当通过UI更改此类几何时,相应的事件称为:
Marker marker = map.createMarker(); GeoPoint position = map.createGeoPoint(lat, lon); marker.setPosition(position); marker.setDraggable(true); map.addMarker(marker); map.addMarkerDragListener(event -> {
在Maps组件中,将JTS几何体添加到Canvas时,相应的方法将返回一个特殊对象,该对象是该几何体在地图上的表示形式: CanvasLayer.Point
, CanvasLayer.Polyline
或CanvasLayer.Polygon
。 该对象具有用于设置各种几何参数的流畅接口,也可以用于订阅与几何相关的事件,或从“画布”中删除几何。
CanvasLayer canvasLayer = map.getCanvas(); CanvasLayer.Point location = canvasLayer.addPoint(address.getLocation()); location.setEditable(true) .setPopupContent(address.getName()) .addModifiedListener(modifiedEvent -> address.setLocation(modifiedEvent.getGeometry()));
几何图
在旧的附加图表和地图中,有一个辅助组件DrawingOptions
。 它用于激活在地图上绘制的功能。 绘制几何图形后,引发相应的事件:
DrawingOptions options = new DrawingOptions(); PolygonOptions polygonOptions = new PolygonOptions(true, true, "#993366", 0.6); ControlOptions controlOptions = new ControlOptions( Position.TOP_CENTER, Arrays.asList(OverlayType.POLYGON)); options.setEnableDrawingControl(true); options.setPolygonOptions(polygonOptions); options.setDrawingControlOptions(controlOptions); options.setInitialDrawingMode(OverlayType.POLYGON); map.setDrawingOptions(options); map.addPolygonCompleteListener(event -> {
地图组件使操作变得更加容易。 新的“ 地图画布”包含一组用于绘制几何图形的方法。 例如,要绘制多边形,请使用canvas.drawPolygon()
方法。 调用此方法后,地图将切换到多边形绘制模式。 该方法接受Consumer<CanvasLayer.Polygon>
函数,您可以在其中对绘制的多边形执行其他操作。
canvasLayer.drawPolygon(polygon -> { territory.setPolygon(polygon.getGeometry()); });
地理分析工具
聚类
新的Maps组件中存在的另一个有用工具是点聚类。 如果图层由大量点组成,则可以启用聚类,将附近的点分组为聚类,以使地图看起来更整洁,更易于感知:

通过在XML描述符的vector
标签内添加cluster
标签来启用cluster
:
<maps:vector id="locations" dataContainer="locationsDc" > <maps:cluster/> </maps:vector>
您还可以基于点权重启用聚类。 点的权重是weightProperty
参数中指定的属性的值。
<maps:vector id="orders" dataContainer="ordersDc" > <maps:cluster weightProperty="amount"/> </maps:vector>
热图
热图是跨多个地理位置的数据密度的直观表示。 addHeatMap(Map<Point, Double> intensityMap)
的可视化组件包含一种添加热图的方法: addHeatMap(Map<Point, Double> intensityMap)
。

结论
地理空间数据的处理,分析和可视化是许多业务应用程序的基本要素。 CUBA Maps组件将为您的CUBA应用程序提供实现此功能所需的所有必要工具。
基于图层的结构有助于构建具有任何内容的地图。 使用tile / WMS图层,您可以将所需的任何提供程序用作基础卡。 向量图层可让您有效地处理同类的地理对象集。 Canvas提供了一个简单的API,用于在地图上显示和绘制几何图形。
该组件可与JTS库中的空间类型配合使用,从而使其与许多其他框架(例如GeoTools )兼容,以解决与地理数据处理和分析有关的各种任务。
我们希望您喜欢该组件。 等待您的反馈!