基于Java和PostGIS的AOI面数据球面面积计算实践
目录
前言
一、计算方法简介
二、球面面积计算
1、AOI数据转Polygon
2、Geotools面积计算
3、GeographicLib面积计算
4、PostGIS面积计算
三、结果分析
1、不同算法结果对比
2、与互联网AOI对比
3、与天地图测面对比
四、总结
前言
在现代地理信息系统(GIS)中,AOI(Area of Interest)面数据的处理和分析是一个核心任务。AOI面数据通常用于表示地理区域,如城市、国家、自然保护区等。准确计算这些区域的面积对于城市规划、环境监测、资源管理等领域至关重要。然而,由于地球的曲率,传统的平面面积计算方法在处理大范围地理数据时会产生显著误差。在实际应用中,AOI面数据的球面面积计算可以应用于多种场景。例如,在城市规划中,准确计算城市区域的面积有助于评估城市扩展的潜力;在环境监测中,计算自然保护区的面积有助于评估生态系统的健康状况;在资源管理中,计算农田的面积有助于评估农业生产的潜力。因此,基于Java和PostGIS的AOI面数据球面面积计算具有广泛的应用前景。因此,球面面积计算成为解决这一问题的关键。
在Java中实现球面面积计算时,可以使用JTS Topology Suite(JTS)库来处理空间数据。JTS提供了丰富的几何操作函数,支持点、线、面等空间数据类型的处理。通过JTS,可以方便地读取AOI面数据,并将其转换为球面坐标系。然后,可以应用球面三角剖分方法,计算每个球面三角形的面积,并累加得到总面积。PostGIS的ST_Area
函数可以直接计算球面面积,但为了验证自定义算法的准确性,可以将Java计算的结果与PostGIS的结果进行对比。通过对比,可以发现两者之间的差异,并进一步优化自定义算法。
本文详细讲解如何基于Java语言,分别使用Geotools、GeographicLib和PostGIS空间函数来分别计算对应的AOI数据的球面面积,为了验证计算结果。我们将采用天地图标绘、互联网查询公开数据的方式来验证空间数据的范围面积。天地图标绘由于采样的点趋向于简单,因此可能采集的精度可能有所损失。而使用互联网的数据则由于可能采集的面范围不一致,存在一定的误差。本例将说明三种不同的方式与实际结果的误差值。为大家对面状的球面面积计算有一个参考。
一、计算方法简介
在实际应用中,AOI面数据通常以矢量数据的形式存储,如Shapefile、GeoJSON等。这些数据包含了地理区域的边界信息,通常由一系列经纬度坐标点组成。为了计算球面面积,需要将这些坐标点转换为球面坐标系,并应用球面几何公式进行计算。PostGIS提供了ST_Area
函数,可以直接计算球面面积,但为了更深入地理解计算过程,可以通过Java实现自定义的球面面积计算算法。球面面积计算的核心在于将地理坐标转换为球面坐标系,并应用球面三角剖分方法。球面三角剖分将AOI面数据划分为多个球面三角形,然后计算每个三角形的面积并累加得到总面积。球面三角形的面积计算可以使用L'Huilier公式或球面余弦定理。这些公式考虑了地球的曲率,能够提供更准确的计算结果。
为了确保球面面积计算的准确性,可以采用多种方法进行验证。例如,可以使用已知面积的地理区域进行测试,比较计算结果与已知面积的差异。此外,还可以使用不同的算法进行对比,如平面面积计算与球面面积计算的对比,以评估球面面积计算的必要性。需要说明的是,在面向GIS的应用当中,对于空间坐标系可以分为地理坐标系和投影坐标系。地理坐标系一般用经纬度标识,它的单位是度,因此直接进行面积计算得出的单位是平方度,是现实中不太好理解的。而投影坐标系是米制单位,通常符合实际的计算结果,如果数据已经是投影坐标,可以直接进行计算就可以得到平方米的面积。
二、球面面积计算
本节将重点介绍在Java和PostGIS空间数据库中来分别进行球面面积计算的方法。计算之前,我们准备了6组对比的数据,这6组数据来自于高德地图中获取的AOI数据。后续我们将会对这六组数据来进行球面面积求解,在后续的对比中也会对这几组数据进行对比。
1、AOI数据转Polygon
在高德地图中,AOI数据是一个收尾坐标连接起来的坐标字符串。因此我们需要对这串坐标字符进行解析,转换成我们的空间面数据,后续针对这类数据来进行面积求解。因此首先我们要将字符串的数据转换成Polygon,这里我们以Geotools为例,讲解如何把字符串数组转换成Polygon,核心代码如下:
/**
* - 将AOI字符串转换成Polygon对象
* @return
*/
public static Polygon convertAoi2Polygon(String aoistr) {String [] AOI_Str_Array = aoistr.split(";");// 获取GeometryFactory实例GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(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);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;
}
2、Geotools面积计算
这里介绍使用Geotools来实现简化的球面面积近似计算,当然这里的地球半径等使用的一个固定常数,6371008.8。首先需要在Maven中引入以下依赖:
<dependency><groupId>org.geotools</groupId><artifactId>gt-referencing</artifactId><version>28.2</version>
</dependency>
然后进行球面面积的近似计算,核心方法如下:
// 简化的球面面积近似计算(实际项目需优化)
private static double approximateSphericalArea(Coordinate p1, Coordinate p2) {// 此处为示例逻辑,真实计算需使用积分或地理公式// 例如:使用 Haversine 公式计算梯形面积double R = 6371008.8; // 地球平均半径(米)double dLon = Math.toRadians(p2.x - p1.x);double lat1 = Math.toRadians(p1.y);double lat2 = Math.toRadians(p2.y);return 0.5 * R * R * dLon * (Math.sin(lat2) + Math.sin(lat1));
}
最后定义一个使用 GeodeticCalculator 逐边计算多边形面积的方法,在这个方法的实现中,我们使用累加的方法来进行计算,核心方法如下:
/*** -使用 GeodeticCalculator 逐边计算多边形面积*/
public static double calculateGeodeticArea(Geometry geometry, CoordinateReferenceSystem crs) throws Exception {if (!(geometry instanceof Polygon)) {throw new IllegalArgumentException("仅支持 Polygon 类型");}Polygon polygon = (Polygon) geometry;Coordinate[] coords = polygon.getExteriorRing().getCoordinates();GeodeticCalculator calculator = new GeodeticCalculator(crs);double totalArea = 0.0;// 使用球面三角形累加算法(类似 JTS 原有逻辑)for (int i = 0; i < coords.length - 1; i++) {Coordinate p1 = coords[i];Coordinate p2 = coords[i + 1];calculator.setStartingGeographicPoint(p1.x, p1.y);calculator.setDestinationGeographicPoint(p2.x, p2.y);// 累加每个边的贡献面积(需自行实现积分逻辑,此处简化)// 实际应参考 JTS 原有算法或使用第三方库(如 GeographicLib)totalArea += approximateSphericalArea(p1, p2);}return Math.abs(totalArea); // 返回绝对值
}
3、GeographicLib面积计算
如果觉得使用Geotools来计算面积比较繁琐,接下来我们使用一个简化的库,利用这个库可以更便捷的对一个面数据的面积进行求解。这个库就是:GeographicLib-Java。首先依然需要的Pom.xml中引用以下依赖:
<dependency><groupId>net.sf.geographiclib</groupId><artifactId>GeographicLib-Java</artifactId><version>1.52</version>
</dependency>
计算球面面积的核心方法更加简单,如下所示:
public static double calculateExactGeodeticArea(Polygon polygon) {Geodesic geodesic = Geodesic.WGS84;PolygonArea areaCalc = new PolygonArea(geodesic, false);for (Coordinate coord : polygon.getExteriorRing().getCoordinates()) {areaCalc.AddPoint(coord.y, coord.x); // 注意经纬度顺序}PolygonResult result = areaCalc.Compute();return Math.abs(result.area); // 单位:平方米
}
4、PostGIS面积计算
在空间数据库中进行球面面积的计算比较简单,为了求解平方米的单位,我们可以将数据转换成geograghy的类型来进行球面的计算,在使用空间面积函数st_area即可实现面积的求解,这里以某小学的空间面数据为例,讲解如何在PostGIS数据库中实现球面面积求解。具体可以体现为以下的SQL:
SELECTst_area ( ST_GeomFromText ( 'POLYGON ((112.87852 28.190856,112.879078 28.190562,112.879613 28.19024,112.880025 28.18993,112.880453 28.189525,112.880568 28.189433,112.880562 28.189324,112.880064 28.188912,112.879793 28.188709,112.879188 28.18857,112.878529 28.188726,112.87848 28.188768,112.878465 28.188882,112.87852 28.190856))', 4326 -- 指定 SRID) :: geography );
在数据库终端运行以下的SQL以后可以得到以下的执行结果,其结果就是面积结果。
32860.61062525073
三、结果分析
这里将统一对生成的结果进行一个简单的对比和分析。通过对比和说明,结合实际的情况,可以看到那种计算方法更加接近实际情况。
1、不同算法结果对比
在应用程序中我们使用6组数据分别调用Geotools和GeographicLib-Java库,其中的一个小学我们还使用了PostGIS的空间函数直接求解的方式。调用代码如下:
public static void main(String[] args) throws NoSuchAuthorityCodeException, FactoryException, Exception {//计算梅溪湖的AOI 面积Polygon polygon = convertAoi2Polygon(MXH_AOI);System.out.println("梅溪湖的球面面积为:" + calculateExactGeodeticArea(polygon));System.out.println("m2 " + calculateGeodeticArea(polygon, CRS.decode("EPSG:4326")));System.out.println("*************************************************************");polygon = convertAoi2Polygon(MXQX_AOI);System.out.println("梅溪青秀的球面面积为:" + calculateExactGeodeticArea(polygon));System.out.println("m2 " + calculateGeodeticArea(polygon, CRS.decode("EPSG:4326")));System.out.println("*************************************************************");polygon = convertAoi2Polygon(YMGY_AOI);System.out.println("杨梅公园的球面面积为:" + calculateExactGeodeticArea(polygon));System.out.println("m2 " + calculateGeodeticArea(polygon, CRS.decode("EPSG:4326")));System.out.println("**************************************************************");polygon = convertAoi2Polygon(BBG_AOI);System.out.println("梅溪新天地的球面面积为:" + calculateExactGeodeticArea(polygon));System.out.println("m2 " + calculateGeodeticArea(polygon, CRS.decode("EPSG:4326")));System.out.println("************************************************************");polygon = convertAoi2Polygon(THL_AOI);System.out.println("桃花岭风景区的球面面积为:" + calculateExactGeodeticArea(polygon));System.out.println("m2 " + calculateGeodeticArea(polygon, CRS.decode("EPSG:4326")));System.out.println("*************************************************************");polygon = convertAoi2Polygon(YYSX_AOI);System.out.println("实验小学的球面面积为:" + calculateExactGeodeticArea(polygon));System.out.println("m2 " + calculateGeodeticArea(polygon, CRS.decode("EPSG:4326")));
}
程序运行结果如下:
这里把面积的结果整理成以下表格。请注意,在以下的表格中,面积的单位是平方米:
序号 | AOI名称 | GeographicLib | Geotools |
1 | 梅溪湖风景区 | 3003365.9397251983 | 3007843.6566917896 |
2 | 梅溪青秀 | 33133.088184464876 | 33182.49462517351 |
3 | 杨梅公园 | 20309.3465869762 | 20339.625071160495 |
4 | 步步高梅溪新天地 | 234694.91455752775 | 235044.35333895683 |
5 | 桃花岭风景区 | 5551323.14381226 | 5559616.194570124 |
6 | 岳麓区实验小学 | 32884.20975263743 | 32933.23007472232 |
通过以上的表格可以看到,两种不同的计算方式的结果还是有一些差异,不过这些差异并不大。
2、与互联网AOI对比
以桃花岭公园为例,桃花岭景区位于湖南长沙岳麓区,是岳麓山风景名胜区的重要组成部分。桃花岭公园规划面积约4360亩。景区内包括洪寺庵水库、腊八寺遗址等重要景点。
将4360亩换算成平方米,大约是3583333.33平方米, 实际上我们算出来的面积有5551323.14381226,当然我们通过高德AOI获取的数据与原始的面积可能存在一些出入,所以导致了在计算与互联网公式面积的一个差异,当然如果有更准去的数据,也可以在评论区留言修正。
3、与天地图测面对比
在上面的小节中,可能出现实际的面积与计算的结果有较大的出入的问题,接下来我们以某小学等为例,使用天地图的测面积方法来实现面积的预估,看与实际的差距有多大,首先来看下天地图中的面积测算。该小学的面积大约是3.46公顷,合34500平方米。
来看后台的计算的结果分别是:32884.20975263743(GeographicLib)和32933.23007472232(Geotools),再来看PostGIS中的计算结果是:32860.61062525073,可以看到对于该小学的面积计算,三者都是比较接近的。同时三者与实际的面积也相差不大。
四、总结
以上就是本文的主要内容。通过实验的方式可以看到,使用PostGIS生成的面积最小,而GeographicLib其次,而使用Geotools的生成是最大的。当然,这与Geotools中的地球半径等客观因素油酸。总之,基于Java和PostGIS的AOI面数据球面面积计算是一个复杂但重要的任务。通过结合Java的编程能力和PostGIS的空间数据处理能力,可以构建一个高效、准确的球面面积计算系统。在实际应用中,需要考虑多种因素,如数据精度、投影方式、地球模型等,并通过多种方法进行验证,以确保计算结果的准确性。通过模块化设计和JDBC集成,可以提高系统的灵活性和可维护性,满足不同应用场景的需求。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。
相关文章:

基于Java和PostGIS的AOI面数据球面面积计算实践
目录 前言 一、计算方法简介 二、球面面积计算 1、AOI数据转Polygon 2、Geotools面积计算 3、GeographicLib面积计算 4、PostGIS面积计算 三、结果分析 1、不同算法结果对比 2、与互联网AOI对比 3、与天地图测面对比 四、总结 前言 在现代地理信息系统(G…...

Spring Boot之Web服务器的启动流程分析
如何判断创建哪种web容器:servlet?reactive? 我们在启动Spring Boot程序的时候,会使用SpringApplication.run方法来启动,在启动流程中首先要判断的就是需要启动什么类型的服务器,是servlet?或者…...
C# SQLite高级功能示例
目录 1 主要功能 2 程序结构和流程 3 详细实现说明 3.1 基础设置 3.2 事务演示 3.3 索引演示 3.4 视图演示 3.5 触发器演示 3.6 全文搜索演示 3.7 窗口函数演示 3.8 外键约束演示 4 高级功能示例 5 单个方法详细介绍 5.1 SetupExampleData()方法 5.2 UseTransact…...

【周输入】510周阅读推荐-1
本号一年了,有一定的成长,也有很多读者和点赞。自觉更新仍然远远不够,需要继续努力。 但是还是要坚持2点: 在当前这个时代,信息大爆炸,层次不齐,不追加多, 信息输入可以很多&#x…...

基于动态规划的强化学习方法
目录 # 动态规划 # 基于动态规划的强化学习方法 # 求解过程: ## 策略评估 ## 策略提升 # 价值迭代算法 # 参考 # 动态规划 动态规划的基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到目标问题的解。…...
启动 spyder ModuleNotFoundError: No module named ‘PyQt5.QtWebKitWidgets‘
一、根本原因 Spyder 版本兼容性:Spyder 4.x 依赖 QtWebKitWidgets,但该模块在 PyQt5 5.15 中已被移除。 PyQt5 版本冲突:如果你安装了较新的 PyQt5(如 5.15),则会缺少 QtWebKitWidgets。 二、解决方案 方法…...
ChemBlender:科研绘图创新解决方案
一、研究背景与冲突 (一)研究背景 在科学研究领域,可视化表达对于成果的呈现与交流至关重要。科研绘图作为科学可视化的关键手段,涵盖了从微观分子结构到宏观实验现象等广泛的内容。随着科研的深入发展,研究对象的复杂…...

Uniapp Android/IOS 获取手机通讯录
介绍 最近忙着开发支付宝小程序和app,下面给大家介绍一下 app 获取通讯录的全部过程吧,也是这也是我app开发中的一项需求吧。 效果图如下 勾选配置文件 使用uniapp开发的童鞋都知道有一个配置文件 manifest.json 简单的说一下,就是安卓/ios/…...
设计一个分布式系统:要求全局消息顺序,如何使用Kafka实现?
一、高吞吐低延迟 Kafka 集群设计要点 1. 分区策略优化 // 计算合理分区数公式(动态调整) int numPartitions max(Tp, Tc) / min(Tp, Tc) // Tp生产者吞吐量 Tc消费者吞吐量建议初始按业务键(如订单ID)哈希分区单分区吞吐建议…...

2025年RIS SCI2区,改进白鲸优化算法+复杂非线性方程组求解,深度解析+性能实测
目录 1.摘要2.白鲸优化算法BWO原理3.改进策略4.结果展示5.参考文献6.代码获取7.读者交流 1.摘要 本文提出了一种改进白鲸优化算法(ABWOA)用来解决非线性方程组(SNLEs)求解问题。ABWOA引入了平衡因子和非线性自适应参数࿰…...

Java后端开发day48--反射动态代理
(以下内容全部来自上述课程) 反射 反射允许对成员变量,成员方法和构造方法的信息进行编程访问。 就是获取里面的成员变量、构造方法和成员方法,idea中打代码跳出来的提示就是反射。 1. 获取class对象的三种方式 Class.for…...
十四、继承与组合(Inheritance Composition)
十四、继承与组合(Inheritance & Composition) 引言 C最引人注目的特性之一是代码复用。组合:在新类中创建已有类的对象。继承:将新类作为已有类的一个类型来创建。 14.1 组合的语法 Useful.h //C14:Useful.h #ifndef US…...

ValueError: Caught ValueError in DataLoader worker process 0.
参考链接: https://stackoverflow.com/questions/1841565/valueerror-invalid-literal-for-int-with-base-10 它提示我有个地方值错误空字符 果然因为格式处理没有传进去东西,找下原因,让它正常处理 原来是相对路径的.影响了程序运行 将v…...

【数据结构】——链表OJ(下)
前面我们已经刷了几道单链表的题目,下面我们继续看几道题目。 一、相交链表 这道题题目的要求是很好理解的,就是现在我们有两个链表,然后我们就相办法进行判断,这两个链表是否是相交的,那么链表的相交其实就是有没有共…...

Adobe Acrobat pro在一份PDF中插入空白页
在Adobe Acrobat pro中先打开我们的PDF文件; 用鼠标点击需要插入空白页处的上一页; 然后如下图操作: 默认会在光标处的下一页插入一张空白页,你也可以修改插入页的页码或者向前一页插入...

java-----异常
对于Error:表示系统级错误或者资源耗尽的状况,像OutOfMemoryError、StackOverflowError等。这类错误是程序无法处理的,通常也不应该尝试去处理。 对于Exception:表示程序可以处理的异常。它又能细分为: 受检查异常&a…...

[工具]B站缓存工具箱 (By 郭逍遥)
📌 项目简介 B站缓存工具箱是一个多功能的B站缓存工具,包含视频下载、缓存重载、文件合并及系统设置四大核心功能。基于yutto开发,采用图形化界面操作,极大简化B站资源获取与管理流程。 工具可以直接将原本缓存的视频读取&#…...
《内网渗透测试:绕过最新防火墙策略》
内网渗透测试是检验企业网络安全防御体系有效性的核心手段,而现代防火墙策略的持续演进(如零信任架构、AI流量分析、深度包检测)对攻击者提出了更高挑战。本文系统解析2024年新型防火墙的防护机制,聚焦协议隐蔽隧道、上下文感知绕…...
python_竞态条件
好的,我们通过一个具体的例子来说明在多线程环境中,可变对象和不可变对象的行为差异,以及不可变对象如何避免竞态条件(race condition)。 1. 竞态条件(Race Condition) 竞态条件是指在多线程环…...
聊聊JetCache的CachePenetrationProtect
序 本文主要研究一下JetCache的CachePenetrationProtect CachePenetrationProtect com/alicp/jetcache/anno/CachePenetrationProtect.java Documented Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD, ElementType.FIELD}) public interface CachePenetr…...
【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全流程)
🚀🔧【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全流程)📊 📑 目录 🚀🔧【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全…...
数据结构:树(Tree)
目录 为什么需要树? 🌱 基本的树结构定义 什么是树? 树的术语 🌿 常见基本树的变体 🌳 二叉搜索树(BST) 🌲 自平衡二叉搜索树 1. AVL树(Adelson-Velsky and La…...

自动化测试与功能测试详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 什么是自动化测试? 自动化测试是指利用软件测试工具自动实现全部或部分测试,它是软件测试的一个重要组成 部分,能完成许多手工测试无…...
java中的Optional
在 Java 8 中,Optional 是一个用于处理可能为 null 的值的容器类,旨在减少空指针异常(NullPointerException)并提升代码的可读性。以下是 Optional 的核心用法和最佳实践: 1. 创建 Optional 对象 1.1 常规创建方式 Op…...
Qt事件循环机制
受事件循环机制影响,按钮的样式表改变了可能不会立即刷新。 需要使用 update() 或 repaint() 或者调用 QApplication::processEvents() 强制处理所有待处理的事件,从而确保界面更新。 在 Qt 中,事件循环(Event Loop)是…...
深入理解 OAuth 2.0:技术核心与实战场景
在互联网应用日益复杂的今天,如何安全、高效地实现第三方应用授权访问资源,成为开发者面临的重要问题。OAuth 2.0 凭借其灵活、安全的授权机制,成为解决这一问题的主流方案。本文将深入剖析 OAuth 2.0 的技术重点,并结合具体使用场…...
Rust 环境变量管理秘籍:从菜鸟到老鸟都爱的 dotenv 教程
前言 写代码的你,是否遭遇过这些灵魂拷问: “我现在在哪个环境?开发?测试?还是直接在生产线上裸奔?”“少写一个 .env,测试脚本在数据库里上演清空大法,客户当场破防。”“每次手动设置 RUST_ENV,命令敲到一半就开始怀疑人生,还怕输错一个字符引发灭世级事故。”别慌…...

CSS经典布局之圣杯布局和双飞翼布局
目标: 中间自适应,两边定宽,并且三栏布局在一行展示。 圣杯布局 实现方法: 通过float搭建布局margin使三列布局到一行上relative相对定位调整位置; 给外部容器添加padding,通过相对定位调整左右两列的…...

OpenCV 的 CUDA 模块中用于将多个单通道的 GpuMat 图像合并成一个多通道的图像 函数cv::cuda::merge
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 OpenCV 的 CUDA 模块中,cv::cuda::merge 函数用于将多个单通道的 GpuMat 图像合并成一个多通道的图像。该函数是 cv::merge 的 GP…...

计网实验笔记(一)CS144 Lab
Lab0 ByteStream : 实现一个在内存中的 有序可靠字节流Lab1 StreamReassembler:实现一个流重组器,一个将字节流的字串或者小段按照正确顺序来拼接回连续字节流的模块Lab2 TCPReceiver:实现入站字节流的TCP部分。Lab3 TCPSender:实…...