目录
前言
一、JavaFx展示原理说明
二、GeoTools的Maven依赖问题
三、引入Geotools相关的资源包
四、创建JavaFx的Canvas实例
五、JavaFx的Scene和Node的绑定
六、总结
前言
众所周知,JavaFx是Java继Swing之后的又一款用于桌面应用的开发利器。当然,现在已经是Web的时代,很多以前需要用桌面软件来支撑的软件,现在都可以使用Web的工具来完成。Web是一个大的趋势,同时现在在这里谈论JavaFx,也并不是说要深入研究。首先还是要根据市场需求来,作为Java开发人员,有可能在实际工作过程中,我们的需求方想要开发一个简单的工具。尤其是面向GIS这样的工具,首先不想像Arcgis那么大,甚至像SuperMap或者MapGIS这样的桌面端软件都不要,需求也是非常简单的,只是进行空间数据的简单可视化,或者做简单的空间分析。同时要求我们的软件是可以在跨平台的环境下运行的。当然除了Java,跨平台的可选择性还有许多。不是说跨平台只有Java能做。
本文以全球各个国家的矢量数据的展示为例,重点讲解如何在JavaFx中进行展示,结合GeoTools来进行展示为需求背景。大家知道,geotools自身的组件框架中,gt_swing是实现桌面应用的一个可视化展示层,虽然在JavaFx中可以将Swing的一些控件进行集成,把Swing的按钮或者表格等放在JavaFx中进行渲染。但是很少把底图放在JavaFx中进行渲染的。当然,哪些使用JavaFx中的WebView嵌入了本地浏览器的不算在内。通过本文,会讲解如何将Geotools的地图在JavaFx中进行展示,大家在实际应用建设过程当中有这个需求的,可以看看博文。
一、JavaFx展示原理说明
本节将对如何使用JavaFx进行空间数据的可视化进行说明,与Swing组件的机制不同。这里将重点讲解这两者是如何结合在一起,最终实现空间数据的可视化的。
上图是一张JavaFx的组件架构图,最下面的是JavaFx的两个基石,Jvm和Java的相关API,这些其实都是它的底层依赖基础。 然后构建在JVM和JDK API基础之上的是相关的组件和开发工具包。这是对不同实现需求的组件封装。最上层是JavaFx的展示层,通过JavaAPI和Scene进行展示,其中Scene场景是JavaFx中一个很重要的对象。因此本文的重点也是围绕Scene来进行,通过Scene的构建和展示,帮助我们来实现空间数据的可视化展示。在实现可视化的过程中,还有一个很重要的对象,就是Canvas画布,我们在JavaFx当中实现可视化的基础,是中间渲染的桥梁。
二、GeoTools的Maven依赖问题
关于GeoTools的Maven依赖问题,由于GeoTools工程项目的相关包不是都在Maven的中央仓库中的,因此我们在配置它的Maven依赖来就跟普通的工程是不一样的。关于GeoTools的maven依赖,其它的很多博主也有相应的文章进行介绍。这里就不再进行赘述。需要申明的是,博客编写过程中,我们的JDK使用的1.8的版本,而对应的Geotools我使用的是28.2的版本。现在Geotools的最新版本应该到31了,大家感兴趣可以到Github中查看一下GeoTools的官方仓库的信息传送门。
应该是31.x的版本是最新的版本。不过为了兼容我们的Jdk的版本,大家用1.8的Jdk,还是可以使用28.x的版本。因此博客提供的案例都是使用28.x版本。
如果我们的maven仓库引用的是阿里云的maven私服的话,那么在仓库的定义上要设置好,否则可能导致geotools的依赖包不能下下来。来看下项目工程的Pom.xml配置。
<repositories>
<!--从22.x后geotools由OSGeo管理-->
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots><enabled>false</enabled></snapshots>
<releases><enabled>true</enabled></releases>
</repository>
<repository>
<id>osgeo-snapshot</id>
<name>OSGeo Snapshot Repository</name>
<url>https://repo.osgeo.org/repository/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
<releases><enabled>false</enabled></releases>
</repository>
<repository>
<id>GeoSolutions</id>
<url>http://maven.geo-solutions.it/</url>
</repository>
</repositories>
三、引入Geotools相关的资源包
在pom.xml中定义了Geotools的依赖仓库地址后,我们来正式引入Geotools的相关依赖。这里为了简化,只提供其具体的配置代码,具体的每个组件的功能,在后面的博客中会进行逐一介绍。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools.xsd</groupId>
<artifactId>gt-xsd-sld</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools.xsd</groupId>
<artifactId>gt-xsd-core</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-xml</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>fxgraphics2d</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
经过上面的步骤,我们基本完成了Geotools官方仓库的定义以及相关依赖包的引用申明,在正常有网络的情况下,系统在后台就会自动帮助我们下载依赖的包。接下来我们来进行具体的功能开发即可。
四、创建JavaFx的Canvas实例
由于在Jdk1.8当中,Javafx还是绑定在一起的,因此我们不需要单独针对JavaFx来进行配置,但是在1.8后面的版本中,jdk就将javaFx和jdk进行了分离,因此要单独进行配置。在引用相应的geotools的资源包之后,我们就可以来创建最重要的Canvas对象,这个Canvas对象是JavaFx的Canvas对象。通过Canvas对象,实现将Map和画布的融合,从而让数据显示处理。因此这里我们定义Canvas对象,并进行地图的初始化等操作。
首先我们创建一个Node对象,这个Node对象包括Canvas、Map和GraphicsContext。同时我们提供两个方法,第一个是进行地图初始化的方法,第二个是进行地图数据绘制的方法。下面是定义Node类的属性和方法。
package tutorial1;
import java.awt.Rectangle;
import java.io.IOException;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.map.FeatureLayer;
import org.geotools.map.MapContent;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.jfree.fx.FXGraphics2D;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class MapCanvas {
private Canvas canvas;
private MapContent map;
private GraphicsContext gc;
public MapCanvas(int width, int height) {
canvas = new Canvas(width, height);
gc = canvas.getGraphicsContext2D();
initMap();
drawMap(gc);
}
public Node getCanvas() {
return canvas;
}
private void initMap() {
}
private void drawMap(GraphicsContext gc) {
}
}
Canvas负责进行展示,而Map对象负责提供显示的数据源。在上述的方法中,我们要先创建一个Canvas对象,在进行对象构造时,指定Canvas对象的高和宽,按照这个尺寸要求来创建。下面我们来看一下如何进行地图的初始化,也就是上面的initMap这个方法。
private void initMap() {
try {
FileDataStore store = FileDataStoreFinder
.getDataStore(this.getClass().getClassLoader().getResource("maps/countries.shp"));
SimpleFeatureSource featureSource = store.getFeatureSource();
map = new MapContent();
map.setTitle("Quickstart");
Style style = SLD.createSimpleStyle(featureSource.getSchema());
FeatureLayer layer = new FeatureLayer(featureSource, style);
map.addLayer(layer);
map.getViewport().setScreenArea(new Rectangle((int) canvas.getWidth(), (int) canvas.getHeight()));
} catch (IOException e) {
e.printStackTrace();
}
}
在进行空间矢量数据的展示时,首先要告诉可视化渲染器,具体的文件在什么位置。接下来是使用样式,为了简单起见,这里使用默认样式来进行可视化,创建样式的时候,样式构建器会根据数据的类型来进行分别构建,比如不同的点、线、面,它的默认样式都是不一样的。这里的数据是面,因此会勾画出边界的线信息。代码如下:
Style style = SLD.createSimpleStyle(featureSource.getSchema());
最后将样式和数据源进行绑定,然后将图层对象设置到map当中。MapContent这个对象就承载着双发的一个展示容器,最后通过返回这个node,将node和JavaFx的scene场景进行融合。再来看一下绘制地图的方法:
private void drawMap(GraphicsContext gc) {
StreamingRenderer draw = new StreamingRenderer();
draw.setMapContent(map);
FXGraphics2D graphics = new FXGraphics2D(gc);
graphics.setBackground(java.awt.Color.WHITE);
Rectangle rectangle = new Rectangle((int) canvas.getWidth(), (int) canvas.getHeight());
draw.paint(graphics, rectangle, map.getViewport().getBounds());
}
在这里,通过JavaFx的Graphics对象就可以实现地图的绘制。
public Node getCanvas() {
return canvas;
}
五、JavaFx的Scene和Node的绑定
虽然在上面的代码中已经实现了将Node对象进行创建,但是还是没有将Node和JavaFx的容器进行绑定,没有Scene的搭载就像没有舞台的演员,想展示自己的才艺都没有舞台。因此最后我们要将Node对象和Scene进行绑定即可。
package tutorial1;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class MainTutorial1 extends Application {
@Override
public void start(Stage primaryStage) {
MapCanvas canvas = new MapCanvas(1090, 670);
Pane pane = new Pane(canvas.getCanvas());
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.setTitle("JavaFx结合Geotools展示地图");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
上面的代码非常简单,通过创建的Canvas对象,然后设置一个地图展示的高度和宽度。将创建的对象和JavaFx的Scene进行绑定,最后设置好JavaFx的应用窗口的标题后,调用Show方法实现空间数据的可视化展示,最后运行的可视化效果如下:
经过以上的步骤,我们就实现了在JavaFx中调用Geotools的相关库实现空间矢量数据的快速预览。 当然,这里只展示了空间可视化的效果。这个程序还是有很多进步的空间,比如需要增加鼠标的拖动和缩放、还要支持样式的定制等操作。时间缘故,下次再深入的来将这些设置,本文到此结束。
六、总结
以上就是本文的主要内容, 本文以全球各个国家的矢量数据的展示为例,重点讲解如何在JavaFx中进行展示,结合GeoTools来进行展示为需求背景。大家知道,geotools自身的组件框架中,gt_swing是实现桌面应用的一个可视化展示层,虽然在JavaFx中可以将Swing的一些控件进行集成,把Swing的按钮或者表格等放在JavaFx中进行渲染。但是很少把底图放在JavaFx中进行渲染的。当然,哪些使用JavaFx中的WebView嵌入了本地浏览器的不算在内。通过本文,会讲解如何将Geotools的地图在JavaFx中进行展示,大家在实际应用建设过程当中有这个需求的,可以看看博文。
本文行文仓促,难免有许多不足之处,还恳请各位专家和朋友们在阅读本文时在评论区留下宝贵意见,鄙人不胜感激。GeoTools非常值得学习,在Java进行GIS开发,一定要掌握这个组件库,提高我们的GIS技术开发能力。