Java调用GDAL实现融合有相同字段属性的多边形矢量数据的一种方法

news2025/2/23 1:08:42

目录

一、写在文章前

二、实现思路

三、实现过程

1.打开矢量数据

 2.生成融合结果

 3.不融合不相邻的几何图形

4.融合后的几何图形写入到新的图层

5.融合的效果

四、完整的示例 


一、写在文章前

如下图所示,融合(dissolve)具有相同字段属性的多边形矢量数据是日常的GIS工作中经常会用到功能,它在数据分析、制图工作中都有重要的作用,目前的GIS软件中均有此功能。个人认为在网络地理信息系统的开发中,使用GDAL是一种性价比较高的方式。据作者所知,GDAL暂时还没有提供融合(dissolve)功能的API,所以作者自己编写了一个用Java调用GDAL实现融合功能的示例程序,在这里共享出来供大家参考、指正。

二、实现思路

一种思路是:

  1. 打开矢量数据,将矢量图层所有的要素存放到一个集合中。
  2. 两次遍历集合将属性值一致的的要素的几何图形(geometry)进行合并(union)操作,并存入融合结果的集合。
  3. 若不希望将不相邻的几何图形(geometry)进行融合,可以遍历融合结果的集合,对于不是环(ring)的几何图形部件逐个将其存入到拆分多部件的集合中,对于是环的几何图形部件,不处理,将其对应的父几何图形直接存入到拆分多部件的集合中。
  4. 将拆分多部件的集合中的要素逐个写入到新的图层,将新的图层存入新的数据中,保存。

这样就得到了融合后的矢量数据。

还有一种方法是对输入的矢量数据执行分组查询的SQL语句,对同一组的数据执行union操作,这样可以替代步骤2,从而减少代码量。GDAL支持的SQL语句语法与SQL99是基本一致的,但需要了解GDAL的空间操作的函数。

本文的实现使用了后者。

三、实现过程

本节主要展示效果及关键代码,完整实现过程在下文。

1.打开矢量数据

        ogr.RegisterAll();
        String dataSet = "E:\\grids.shp";
        String outPath = "E:\\grids_merge.shp";

注册了所有的驱动,指定了输入和输出文件。

输入的数据是一个用QGIS生成的网格,存在一个字段属性”grade“。几何图形如下图所示。

        DataSource ds = ogr.Open(dataSet);
        Layer layer = ds.GetLayerByName(dataName);

这是打开数据源和图层的代码。 

 2.生成融合结果

        //拼接SQL语句,严格一点的话,可能需要用JDBC先预编译SQL语句,这里简化了。
        String sql = "select ST_Union(GEOMETRY) AS GEOMETRY,grade,count(*) as count from " + dataName + " group by grade";
        // 执行SQL语句
        Layer layerSQL = ds.ExecuteSQL(sql, null, "SQLITE");
        //通过ExecuteSQL获取的Layer必须在销毁数据源之前使用ReleaseResultSet销毁。
        ds.ReleaseResultSet(layerSQL);

根据GDAL的API文档要求对于执行SQL获取的图层,必须要执行ReleaseResultSet销毁。销毁代码需要放在销毁数据源的代码之前。

 3.不融合不相邻的几何图形

如果需要不融合不相邻的几何图形,还需要下面的代码。

        Feature feature;
        Map<Geometry, Feature> map = new HashMap<>();
        //将执行SQL语句后的图层要素中的”多部件“几何图形分割为单部件
        while ((feature = layerSQL.GetNextFeature()) != null) {
            Geometry geometry = feature.GetGeometryRef();
            int count = geometry.GetGeometryCount();
            for (int i = 0; i < count; i++) {
                Geometry part = geometry.GetGeometryRef(i);
                boolean isRing = part.GetGeometryType() == ogrConstants.wkbLineString || part.GetGeometryType() == ogrConstants.wkbMultiLineString;

                if (isRing) {
                    map.put(geometry, feature);
                    break;
                } else {
                    map.put(part, feature);
                }
            }
        }

4.融合后的几何图形写入到新的图层

将融合后的几何图形写入新的图层,对于不需要不融合不相邻的几何图形的情况,只需要复制SQL执行结果的图层到新数据源即可。

DataSource outDataSource = driver.CreateDataSource(outPath);
outDataSource.CopyLayer(layerSQL,name,options);

对于需要不融合不相邻的几何图形,则需要执行下列代码。

        for (int i = 0; i < layerSQL.GetLayerDefn().GetFieldCount(); i++) {
            FieldDefn fieldDefn = layerSQL.GetLayerDefn().GetFieldDefn(i);
            layer1.CreateField(fieldDefn);
        }

        for (Map.Entry<Geometry, Feature> entry : map.entrySet()) {
            Feature feature1 = entry.getValue();
            Feature feature2 = new Feature(feature1.GetDefnRef());

            for (int j = 0; j < feature1.GetFieldCount(); j++) {
                //复制属性表中的数据,这里简写了,只考虑的短整形的一种情况
                //对shapefile而言,实际还有长整形、浮点型、双精度、文本、日期等数据类型
                feature2.SetField(j, feature1.GetFieldAsInteger(j));
            }
            Geometry geometry2 = entry.getKey();

            feature2.SetGeometry(geometry2);
            layer1.CreateFeature(feature2);
        }

保存数据的代码如下 

        ds.ReleaseResultSet(layerSQL);
        outDataSource.SyncToDisk();

        ds.delete();
        outDataSource.delete();

5.融合的效果

融合数据的几何图形如下图所示:

融合数据的几何图形属性表如下: 

四、完整的示例 

需要说明的是,本例子也只是简单的实现,距离在生产环境使用还有一定的差距,仅供学习使用。

import org.gdal.gdal.gdal;
import org.gdal.ogr.*;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

public class GDALMergeByAttribute {
    public static void main(String[] args) {
        ogr.RegisterAll();

        String dataSet = "E:\\grids.shp";
        String outPath = "E:\\grids_merge.shp";

        System.out.printf("开始融合数据!输入文件:%s\n", dataSet);
        // 打开数据源
        DataSource ds = ogr.Open(dataSet);

        if (ds == null) {
            System.out.println("打开数据源失败");
            return;
        }
        //一般GIS软件生成shapefile的”图层名“和文件名一般是一致的
        File dataSetFile = new File(dataSet);
        String dataName = dataSetFile.getName();
        dataName = dataName.substring(0, dataName.lastIndexOf("."));

        Layer layer = ds.GetLayerByName(dataName);
        if (layer == null) {
            System.out.println("打开图层失败");
            return;
        }


        //拼接SQL语句,严格一点的话,可能需要用JDBC先预编译SQL语句,这里简化了。
        String sql = "select ST_Union(GEOMETRY) AS GEOMETRY,grade,count(*) as count from " + dataName + " group by grade";
        System.out.printf("SQL语句的内容为:%s\n", sql);
        // 执行SQL语句
        Layer layerSQL = ds.ExecuteSQL(sql, null, "SQLITE");
        if (0 != gdal.GetLastErrorNo()) {
            System.out.println("执行SQL语句出错!");
            return;
        }
        System.out.printf("执行SQL语句成功!返回数据行数:%s\n", layerSQL.GetFeatureCount());
        Driver driver = ds.GetDriver();
        Vector<String> options = new Vector<>();
        //防止属性表中文乱码
        options.add("ENCODING=UTF-8");


        File file = new File(outPath);
        String name = file.getName();
        name = name.substring(0, name.lastIndexOf("."));
        Feature feature;
        Map<Geometry, Feature> map = new HashMap<>();
        //将执行SQL语句后的图层要素中的”多部件“几何图形分割为单部件
        while ((feature = layerSQL.GetNextFeature()) != null) {
            Geometry geometry = feature.GetGeometryRef();
            int count = geometry.GetGeometryCount();
            for (int i = 0; i < count; i++) {
                Geometry part = geometry.GetGeometryRef(i);
                boolean isRing = part.GetGeometryType() == ogrConstants.wkbLineString || part.GetGeometryType() == ogrConstants.wkbMultiLineString;
                if (isRing) {
                    map.put(geometry, feature);
                    break;
                } else {
                    map.put(part, feature);
                }
            }
        }

        System.out.printf("融合后的要素:%s个,分割不相邻要素后要素有%s个\n", layerSQL.GetFeatureCount(), map.size());
        DataSource outDataSource = driver.CreateDataSource(outPath);
        Layer layer1 = outDataSource.CreateLayer(name, layerSQL.GetSpatialRef(), layer.GetGeomType(), options);


        for (int i = 0; i < layerSQL.GetLayerDefn().GetFieldCount(); i++) {
            FieldDefn fieldDefn = layerSQL.GetLayerDefn().GetFieldDefn(i);
            layer1.CreateField(fieldDefn);
        }
        System.out.printf("创建属性字段成功!字段数:%s\n", layer1.GetLayerDefn().GetFieldCount());

        for (Map.Entry<Geometry, Feature> entry : map.entrySet()) {
            Feature feature1 = entry.getValue();
            Feature feature2 = new Feature(feature1.GetDefnRef());

            for (int j = 0; j < feature1.GetFieldCount(); j++) {
                //复制属性表中的数据,这里简写了,只考虑的短整形的一种情况
                //对shapefile而言,实际还有长整形、浮点型、双精度、文本、日期等数据类型
                feature2.SetField(j, feature1.GetFieldAsInteger(j));
            }
            Geometry geometry2 = entry.getKey();

            feature2.SetGeometry(geometry2);
            layer1.CreateFeature(feature2);
        }
        System.out.printf("新图层写入要素:%s\n", layer1.GetFeatureCount());
        //通过ExecuteSQL获取的Layer必须在销毁数据源之前使用ReleaseResultSet销毁。
        ds.ReleaseResultSet(layerSQL);
        outDataSource.SyncToDisk();
        System.out.printf("融合数据成功!文件名:%s\n", outDataSource.GetName());

        ds.delete();
        outDataSource.delete();

    }

}

 正常情况下的输出:

开始融合数据!输入文件:E:\grids.shp
SQL语句的内容为:SELECT ST_Union(GEOMETRY) AS GEOMETRY,grade,count(*) as count FROM grids group by grade
执行SQL语句成功!返回数据行数:3
融合后的要素:3个,分割不相邻要素后要素有4个
创建属性字段成功!字段数:2
新图层写入要素:4
融合数据成功!文件名:E:\grids_merge.shp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1161465.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux中搭建coturn服务器

1、下载coturn源码 git clone https://github.com/coturn/coturn.git2、进入到coturn路径下&#xff0c;执行一下命令。 ./configure出现以下错误&#xff1a; 问题1&#xff1a;ERROR: OpenSSL Crypto development libraries are not installed properly in required locati…

浅谈安科瑞无线测温产品在南非某变电站的应用

摘要&#xff1a;随着电力工业的发展&#xff0c;对设备的安全性、可靠性要求越来越高。在这种条件下&#xff0c;高压设备的无线测温系统应运而生。这种技术是将内置电池或电流感应和无线发射模块的测温传感器安装于各测温点&#xff0c;由于其体积小&#xff0c;且无需任何接…

CHS零壹视频恢复程序监控版/海康版/大华版深入扫描功能演示

安防文件系统是一种嵌入式文件系统&#xff0c;一般情况下监控版程序扫描会基于文件系统进行扫描&#xff0c;如果想更深入的扫描一些数据建议开启深入扫描功能&#xff0c;具体方法如下: 适用版本:监控版/海康版/大华版/专业版/高级版 作用&#xff1a;舍弃嵌入式文件系统直…

手机端抓包流程

一.Charles 抓包 1.创建模拟器 2.配置证书 3.打开开发者工具中的usb调试 4.下载xposed安装 5.安装模块 破解app反代理 使用XposedJustTrustMe突破SSL Pinning验证_justtrustme模块官网-CSDN博客 配置流程 手机APP数据包抓包分析_socksdroid下载-CSDN博客 二.HttpCanary抓…

【CIO人物展】申菱环境CIO吴斌:制造业智能化转型,从3个“三”开始

吴斌 本文由申菱环境CIO吴斌投递并参与《2023中国数智化转型升级优秀CIO》榜单/奖项评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 近日&#xff0c;苏州市举办了苏州市智能制造生态大会暨苏州市智能制造产业联盟第二届二次会员大会&#xff0c;苏州市工信局副局长万资…

【观察】从口袋到云端全景式AI创新,联想“全栈智能”再升级

知名科技杂志《连线》创始主编凯文凯利曾预测&#xff1a;“在未来的 100 年里&#xff0c;人工智能将超越任何一种人工力量&#xff0c;将人类引领到一个前所未有的时代。” 确实如此&#xff0c;犹如历史上蒸汽机、电力、计算机和互联网等通用技术一样&#xff0c;近20年来&a…

哪个女人不是在事业和家庭间走钢丝?

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 小黑 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩天津录音间 这是一场因2023年诺贝尔经济学奖获得者克劳迪娅戈尔丁著作《事业还是家庭&#xff1f;女性追求平等的百年…

12 _ 排序(下):如何用快排思想在O(n)内查找第K大元素?

上一节我讲了冒泡排序、插入排序、选择排序这三种排序算法,它们的时间复杂度都是O(n2),比较高,适合小规模数据的排序。这里会介绍两种时间复杂度为O(nlogn)的排序算法,归并排序和快速排序。这两种排序算法适合大规模的数据排序,比上一节讲的那三种排序算法要更常用。 归并…

PDF 表单直接保存到您的文档中--TX Text Control

TX Text Control .NET Server for ASP.NET Document Viewer 32.0.2 允许用户保存包含已填写表单字段的文档&#xff0c;从而更轻松地协作和共享信息。 TX Text Control .NET Server for ASP.NET 是一个适用于 ASP.NET 和 ASP.NET Core 的综合服务器端文档处理库。功能包括 PDF …

5.2 用户数据报协议UDP

思维导图&#xff1a; 课程笔记&#xff1a;5.2 用户数据报协议UDP 5.2.1 UDP概述 一、UDP基本概念 无连接协议&#xff1a;UDP是一个简单的面向数据报的传输层协议&#xff0c;不需要在数据传输前建立连接&#xff0c;故减少开销和延迟。复用/分用&#xff1a;UDP允许多个应…

记录rider编辑器快速文档 中英文显示的问题

起初是不同的项目里快速文档一个项目显示中文 一个项目显示英文 搞了很久不知道哪里的原因 偶然灵机一动,点开了下面docs.microsoft.com的地址进去一看 发现一个是4.6的文档 一个是4.6.1的文档 所以去项目属性里 切换了framework的版本. 然后汉化就好了 纯属强迫症,而且…

shell script 学习案例

或许进度最快的方法就是先抄袭&#xff0c;在改进&#xff0c;哈哈。学习下各个案例吧 第一&#xff1a;写过程序的都知道&#xff0c;第一个学的就是输出"Hello World!",那么学习shell也是一样&#xff0c;上案例 因为有脚本文件&#xff0c;所有同时 开了两个终端…

13 _ 线性排序:如何根据年龄给100万用户数据排序?

前两节中,着重分析了几种常用排序算法的原理、时间复杂度、空间复杂度、稳定性等。这节,将讲三种时间复杂度是O(n)的排序算法:桶排序、计数排序、基数排序。因为这些排序算法的时间复杂度是线性的,所以我们把这类排序算法叫作线性排序(Linear sort)。之所以能做到线性的时…

光刻机之父林本坚发声,中国芯片可自主研发5纳米,美国拦不住

外媒报道指早已退休的台积电前研发副总林本坚接受采访的时候表示&#xff0c;美国无法阻止中国研发先进工艺&#xff0c;还认为中国可以利用现有设备研发更先进的5纳米工艺&#xff0c;他的表态让外媒相当吃惊。 林本坚如此说法在中国的华为研发出相当于7纳米工艺的麒麟9000S之…

Linux常用命令——chfn命令

在线Linux命令查询工具 chfn 用来改变finger命令显示的信息 补充说明 chfn命令用来改变finger命令显示的信息。这些信息都存放在/etc目录里的passwd文件里。若不指定任何选项&#xff0c;则chfn命令会进入问答式界面。 语法 chfn(选项)(参数)选项 -f<真实姓名>或-…

玩转硬件之Micro:bit的玩法(三)——计步器

随着技术的发展&#xff0c;现在智能手机和智能手表已经走进千家万户&#xff0c;所以大家对于计步器可能不陌生&#xff0c;计步器是一种用于计算行走步数的装置。它通常是一个小型电子设备&#xff0c;可以佩戴在身体上&#xff0c;如腕带、腰带或口袋中。计步器通过感应人体…

代码随想录算法训练营第四十一天丨 动态规划part04

01背包理论基础 见连接&#xff1a;代码随想录 416. 分割等和子集 思路 01背包问题 背包问题&#xff0c;大家都知道&#xff0c;有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解…

matlab 计算Ax=b的解,解线性方程组的现成工具

只写了最简单的方式&#xff0c;其中b需要是列向量&#xff0c;用分号隔开元素&#xff1b; octave:7> A[1,2; 1.0001, 2;] A 1.0000 2.00001.0001 2.0000octave:8> b[3; 3.0001;] b 3.00003.0001octave:9> xA\b x 1.00001.0000octave:10> b-A*x ans 00octave:…

精通Nginx(03)-配置简述

本文主要讲述Nginx配置文件结构及调试技巧 使用nginx版本为1.24.0。 目录 Nginx目录 nginx.conf内容结构 配置片段化 配置调试技巧 Nginx目录 Nginx编译安装目录如下&#xff1a; 安装指定目录为"/usr/local"。配置目录为/usr/local/nginx/conf。 目录说明&am…

在NestJS应用程序中使用 Unleash 实现功能切换的指南

前言 近年来&#xff0c;软件开发行业迅速发展&#xff0c;功能开关&#xff08;Feature Toggle&#xff09;成为了一种常见的开发实践。通过功能开关&#xff0c;可以在运行时动态地启用或禁用应用程序的特定功能&#xff0c;以提供更灵活的软件交付和配置管理。对于使用 Nes…