在Java中基于Geotools对PostGIS数据库的空间查询实践教程

2025-05-27 15:50

本文主要是介绍在Java中基于Geotools对PostGIS数据库的空间查询实践教程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环...

前言

在当今数字化浪潮下,空间数据的应用价值日益凸显,从城市规划到环境监测,从物流配送到地理信息系统(GIS)开发,精准、高效的空间数据查询成为关键环节。而 Java 作为广泛应用的编程语言,在与地理空间技术的融合中展现出独特魅力。Geotools 作为开源的 Java GIS 库,为 Java 开发者提供了强大的地理空间数据处理能力,犹如一把开启空间数据宝藏之门的钥匙。PostGIS 则是 PostgreSQL 数据库的空间扩展,能够存储和处理复杂的空间数据类型,是空间数据存储与管理的得力助手。将 Geotools 与 PostGIS 结合,意味着我们可以利用 Java 强大的编程生态和 Geotools 丰富的 GIS 功能,深度挖掘 PostGIS 中海量空间数据的潜力。

比如我们需要对某一个风景区的商业开发程度进行评价,需要做的第一件事就是根据风景区的范围也就是AOI数据,查询这个AOI数据的范围内所有的POI数据,然后根据不同的POI类型进行分类,从而计算不同的POI类型在景区内的分布情况,加上一些空间分析和相关计算,从而可以对当前景区的一个商业情况进行初步的评估,从而为景区的优质发展提供指导和参考,以下图为例:

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

本博客将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在 Java 环境下借助 Geotools 驾驭 PostGIS 数据库,实现高效精准的空间数据检索,为相关领域开发者提供实用的技术路径,助力空间数据应用的创新拓展。

一、相关技术背景介绍

这里以长沙岳麓山风景区为例,主要介绍如何得出岳麓山及风景区内的POI数据的分布情况。从而为下一步对该风景区的商业及人文指数进行评价。由此我们需要对岳麓山风景区的空间范围数据,以及有了AOI范围后对其范围内的POI数据进行检索的流程进行简单说明。

1、评价对象AOI

关于如何获取AOI数据,在之前的博文中我们曾经进行过相应的说明。大家可以从相应的官方渠道获取。也可以从互联网图源来获取,比如从百度地图或者高德地图中也可以获取数据。以高德地图为例,在下面的查询界面中可以查看到具体的数据:

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

我们可以在网络请求的地方对这个空间面数据进行调试,也可以将这个数据复制到我们的开发环境中,从而可以实现离线的空间计算。如下图所示:

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

上面的数据也是本博客的目标AOI范围,我们后续的所有工作都将围绕这个区域来展开。 因此,如果对AOI数据不是很了解,建议先对相关知识又一个大体的认识以便于更好的掌握相关知识。

2、数据处理流程

为了让大家对整个数据处理的流程有一个简单的认识,这里将整个数据的处理流程给读者进行分享,大致的流程步骤如下:

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

从整体来说,分为三个阶段。第一个阶段是AOI即空间查询面的构建,第二界面是基于AOI面的POI检索,第三阶段就是将两个图层进行数据叠加后渲染出图。 下面将结合流程图对重要的处理节点的逻辑进行详细的介绍。

二、对AOI空间范围查询实践

本节将重点介绍如何对AOI数据进行空间范围查询的实现进行说明。对应前文提到的三个阶段来分别展开,从查询目标的空间查询构建到空间样式创建,最后对整体成果进行出图逐级展开。

1、空间查询构建

空间查询的构建比较简单,主要包含三个环节的工作,第一个是将字符串类型的AOI数据进行解析,刚开始是从高德地图中获取AOI字符串,由于是高德地图的坐标,因此我们首先要对坐标进行转换,将其转换为我们熟悉的WGS84的地理坐标,最后再将转换好的坐标来构建一个完整的查询面,关键代码如下所示:

/**
* - 将AOI字符串转换成Polygon对象
* @return
*/
public static Polygon convertAoi2Polygon(String aoistr) {
	String [] AOI_Str_Array = aoistr.split(";");
	// 获取GeometryFactory实例
    GeometryFactory geometryFactory = JTSFactoryFinder.getGeChina编程ometryFactory(null);
    Coordinate[] coords = {};
    if( AOI_Str_Array.length > 0) {
        coords = new Coordinate[AOI_Str_Array.length];
    }
    //处理坐标
    for (int i = 0; i < AOI_Str_Array.length; i++) {
        String loc = AOI_Str_Array[i];
        String [] latlon = loc.split(",");
		double lng = Double.parseDouble(latlon[0]);
		double lat = Double.parseDouble(latlon[1]);
		//将高德坐标转换成WGS84坐标
		double [] gcj284 = CoordinateTransformUtil.gcj02towgs84(lng, lat);
		//System.out.println("高德坐标转wgs84坐标" + gcj284[0] + "=" + gcj284[1]);
		coords[i] = new Coordinate(gcj284[0], gcj284[1]);
	}
    LinearRing shell = geometryFactory.createLinearRing(coords);
    Polygon polygon = geometryFactory.createPolygon(shell, null);
    polygon.setSRID(4326);//设置4326的坐标
    return polygon;
}

经过前面的步骤,我们已经成功的合成了我们的查询空间目标,接下来就需要基于Geotools来构建对PostGIS数据库的空间查询API, 为了演示我们本地的POI功能,这里我们使用本地的POI信息表,当然这张表的数据可能与实际情况有一定的出入,仅做参考。关键代码如下:

// 2. 创建PostGIS数据存储
DataStore dataStore = createPostgisDataStore();
// 3. 二次查询:用该多边形查询点数据(如查询橘子洲景区内的POI)
String poiLayerName = "biz_poi_info";
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
PropertyName geomProperty = ff.property("geom"); // 点数据的几何列名
// 4、空间关系:点在多边形内(WITHIN)或相交(INTERSECTS)
Filter spatialFilter = ff.within(geomProperty, ff.literal(aoiPolygon));
Query pointQuery = new Query(poiLayerName, spatialFilter);
FeatureSource pointSource = dataStore.getFeatureSource(poiLayerName);

为了方便查看是否成功的执行了查询,这里我们将查询结果进行打印输出。可以在控制台看到很多的信息输出,如下图所示 :

System.out.println("POI数量:"+points.size());
// 6. 处理结果
try (FeatureIterator iterator = points.features()) {
    while (iterator.hasNext()) {
       Feature pointFeature = iterator.next();
       Geometry point = (Geometry) pointFeature.getDefaultGeometryProperty().getValue();
       System.out.println("POI坐标: " + point.getCoordinate());
       printSimpleFeatureAttributes((SimpleFeature)pointFeature); // 打印属性
       System.out.println("------------------------------------------------------");
    }
}

运行后在IDE的控制台中可以看到以下输出:

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

能看到以上结果说明我们的空间查询函数构建正确,可以正常执行。 

2、空间样式创建

为了能让展示的效果更好,因此我们需要对获取的AOI数据面和AOI数据面内的POI数据进行标绘,这里我们需要使用SLD的方式来进行美化,两个生成空间样式的方php法如下:

 public static Style createDashedBorderStyle() {
        StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
        PolygonSymbolizer symbolizer = sf.createPolygonSymbolizer(
        		sf.createStroke(ff.literal(Color.DARK_GRAY), ff.literal(0.8)),
                sf.createFill(ff.literal(Color.BLUE), ff.literal(0.8)), // 80%透明度
                null
        );
        Rule rule = sf.createRule();
        rule.symbolizers().add(symbolizer);
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        return style;
    }
    private static Style createPoiStyle() {
        StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
        // 创建圆形符号
        Mark mark = sf.createMark();
        mark.setWellKnownName(ff.literal("circle"));
        mark.setFill(sf.createFill(ff.literal(Color.RED)));
        mark.setStroke(sf.createStroke(ff.literal(Color.BLACK), ff.literal(1)));
        Graphic graphic = sf.createDefaultGraphic();
        graphic.graphicalSymbols().clear();
        graphic.graphicalSymbols().add(mark);
        PointSymbolizer pointSym = sf.createPointSymbolizer(graphic, null);
        // 新增震级标注
        Font font = sf.createFont(ff.literal("楷体"),ff.literal("Regular"),ff.literal("normal"),ff.literal(18));
        // 创建文本标注
       TextSymbolizer textSym = sf.createTextSymbolizer(
                sf.createFill(ff.literal(Color.WHITE)),
                new Font[] { font },
                null,
                ff.property("name"),  // 标注字段
                null,
                null
        );
        // 标注位置(点右侧偏移)
        AnchorPoint anchor = sf.createAnchorPoint(ff.literal(-0.05), ff.literal(0.05));
        Displacement displacement = sf.createDisplacement(ff.literal(0.1), ff.literal(0));
        Fill textFill = sf.createFill(ff.literal(Color.RED));
        Halo halo = sf.createjsHalo(
            sf.createFill(ff.literal(Color.WHITE)),
            ff.literal(1)
        );
 android       textSym.setFont(font);
        textSym.setFill(textFill);
        textSym.setHalo(halo);
        //新的设置方法
        PointPlacement placement = sf.createPointPlacement(anchor, displacement, ff.literal(0));
        textSym.setLabelPlacement(placement);
        Rule rule = sf.createRule();
        rule.symbolizers().add(pointSym);
        rule.symbolizers().add(textSym);
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        return style;
 javascript   }

创建了以上的样式之后,我们就可以将样式和数据进行融合,这样就能绘制出漂亮的地图了。 

3、成果出图

在完成查询数据的转换以及空间查询的实现等,接下来就是揭晓答案的时候,我们在代码层面实现了对岳麓山的AOI构建以及其AOI包围的POI数据,关键代码如下:

SimpleFeatureCollection poiCollection = (SimpleFeatureCollection) pointSource.getFeatures(pointQuery);
// 7. 创建样式
Style aoiStyle = createDashedBorderStyle();
 Style poiStyle = createPoiStyle();
// 8. 创建地图内容
MapContent mapContent = new MapContent();
SimpleFeatureSource aoiSfs = convert(aoiPolygon);
mapContent.addLayer(new FeatureLayer(aoiSfs, aoiStyle));
mapContent.addLayer(new FeatureLayer(poiCollection, poiStyle));
// 9. 设置输出范围和尺寸
ReferencedEnvelope mapBounds = poiCollection.getBounds();
mapBounds.expandBy(0.2); // 扩展边界
// 10、输出图像大小(例如:宽度x高度)
int width = 1920; // 可根据需求调整		
// 计算地理宽高比
double ASPectRatio = mapBounds.getWidth() / mapBounds.getHeight();
//根据比例计算新高度
int height = (int) Math.round(width / aspectRatio);
// 渲染图片
BufferedImage image = renderMap(mapContent, aoiSfs.getBounds(), width, height);
// 保存图片
ImageIO.write(image, "png", new File("D:/AOI及其包含POI数据示意图.png"));
System.out.println("finished");  
dataStore.dispose();

使用main函数或者测试用例都可以执行以上的代码,程序运行后可以在对应的磁盘目录下看到以下的成果:

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

从上图中可以明显看到,在岳麓上上,一些POI的分布基本还是比较集中的,比如东北方向, 中间的区域也是非常多。当然,把POI数据展现在地图上还只是一个阶段,要想实现商业化,评估。在有了POI数据之后,还要结合数据量,聚类等方面进行空间的分析,且听我们下回分解。

三、总结

以上就是本文的主要内容,本博客将深入探讨基于Geotools对PostGIS数据库的空间查询实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在 Java 环境下借助 Geotools 驾驭 PostGIS 数据库,实现高效精准的空间数据检索,为相关领域开发者提供实用的技术路径,助力空间数据应用的创新拓展。如果您也需要对一个AOI数据进行范围内的POI进行分析查询,并且使用的GeoTools的基础PostGIS访问功能,那么您可以使用以上实现过程和代码进行调试。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。

到此这篇关于在Java中基于Geotools对PostGIS数据库的空间查询实践的文章就介绍到这了,更多相关java Geotools PostGIS空间查询内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于在Java中基于Geotools对PostGIS数据库的空间查询实践教程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154805

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三