A method for Java to call GDAL to fuse polygon vector data with the same field attributes

Table of Contents

1. Write before the article

2. Implementation ideas

3. Implementation process

1. Open vector data

2. Generate fusion results

3. Do not fuse non-adjacent geometric figures

4. Write the merged geometry to a new layer

5. The effect of fusion

4. Complete example


1. Write before the article

As shown in the figure below, fusing (dissolve) polygon vector data with the same field attributes is a function often used in daily GIS work. It plays an important role in data analysis and cartography, and is available in current GIS software. this function. Personally, I think using GDAL is a cost-effective way to develop a network geographic information system. As far as the author knows, GDAL does not yet provide an API for the dissolve function, so the author wrote a sample program that uses Java to call GDAL to implement the dissolve function. It is shared here for your reference and correction.

2. Implementation ideas

One way of thinking is:

  1. Open the vector data and store all the features of the vector layer into a collection.
  2. Traverse the collection twice to perform a union operation on the geometries of elements with consistent attribute values, and store them in the collection of fusion results.
  3. If you do not want to fuse non-adjacent geometries, you can traverse the collection of fusion results, and store the geometric components that are not rings one by one into a set of split multiple components. For those that are rings, The geometric components are not processed, and their corresponding parent geometries are directly stored in the collection of split multi-components.
  4. Write the elements in the split multi-part collection to new layers one by one, store the new layers into new data, and save.

In this way, the fused vector data is obtained.

Another method is to execute a group query SQL statement on the input vector data and perform a union operation on the same group of data. This can replace step 2 and reduce the amount of code. The SQL statement syntax supported by GDAL is basically consistent with SQL99, but you need to understand GDAL’s spatial operation functions.

The implementation of this article uses the latter.

3. Implementation process

This section mainly displays the effects and key codes. The complete implementation process is below.

1.Open vector data

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

All drivers are registered and input and output files are specified.

The input data is a grid generated with QGIS, and there is a field attribute “grade”. The geometry is shown below.

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

This is the code to open the data source and layer.

2. Generate fusion results

 //Splicing SQL statements. To be more strict, you may need to use JDBC to precompile the SQL statements. This is simplified.
        String sql = "select ST_Union(GEOMETRY) AS GEOMETRY,grade,count(*) as count from " + dataName + " group by grade";
        //Execute SQL statement
        Layer layerSQL = ds.ExecuteSQL(sql, null, "SQLITE");
        //The Layer obtained through ExecuteSQL must be destroyed using ReleaseResultSet before destroying the data source.
        ds.ReleaseResultSet(layerSQL);

According to GDAL’s API documentation, the layer obtained by executing SQL must be destroyed by ReleaseResultSet. The destruction code needs to be placed before the code that destroys the data source.

3. Do not merge non-adjacent geometric figures

If you need not to merge non-adjacent geometries, you also need the following code.

 Feature feature;
        Map<Geometry, Feature> map = new HashMap<>();
        //Split the "multi-part" geometry in the layer features after executing the SQL statement into single parts
        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. The fused geometry is written to a new layer

Write the merged geometry into a new layer. If you do not need to merge non-adjacent geometries, you only need to copy the layer of the SQL execution result to the new data source.

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

For non-adjacent geometries that need not to be merged, the following code needs to be executed.

 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 + + ) {
                //Copy the data in the attribute table, abbreviated here, only consider the case of short shaping
                //For shapefiles, there are actually data types such as long integer, floating point, double precision, text, and date.
                feature2.SetField(j, feature1.GetFieldAsInteger(j));
            }
            Geometry geometry2 = entry.getKey();

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

The code to save data is as follows

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

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

5. Effect of fusion

The geometry of the fused data is shown below:

The geometric attribute table of the fused data is as follows:

4. Complete example

It should be noted that this example is only a simple implementation and is still far from being used in a production environment. It is only for learning purposes.

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("Start merging data! Input file: %s\
", dataSet);
        //Open data source
        DataSource ds = ogr.Open(dataSet);

        if (ds == null) {
            System.out.println("Failed to open data source");
            return;
        }
        //Generally, the "layer name" and file name of the shapefile generated by GIS software are generally the same.
        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("Failed to open layer");
            return;
        }


        //Splicing SQL statements. To be more strict, you may need to use JDBC to precompile the SQL statements. This is simplified.
        String sql = "select ST_Union(GEOMETRY) AS GEOMETRY,grade,count(*) as count from " + dataName + " group by grade";
        System.out.printf("The content of the SQL statement is: %s\
", sql);
        //Execute SQL statement
        Layer layerSQL = ds.ExecuteSQL(sql, null, "SQLITE");
        if (0 != gdal.GetLastErrorNo()) {
            System.out.println("Error executing SQL statement!");
            return;
        }
        System.out.printf("Execute SQL statement successfully! Number of data rows returned: %s\
", layerSQL.GetFeatureCount());
        Driver driver = ds.GetDriver();
        Vector<String> options = new Vector<>();
        //Prevent Chinese garbled characters in the attribute table
        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<>();
        //Split the "multi-part" geometry in the layer features after executing the SQL statement into single parts
        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("Features after fusion: %s, after splitting non-adjacent features, there are %s elements\
", 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("Create attribute field successfully! Number of fields: %s\
", 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 + + ) {
                //Copy the data in the attribute table, abbreviated here, only consider the case of short shaping
                //For shapefiles, there are actually data types such as long integer, floating point, double precision, text, and date.
                feature2.SetField(j, feature1.GetFieldAsInteger(j));
            }
            Geometry geometry2 = entry.getKey();

            feature2.SetGeometry(geometry2);
            layer1.CreateFeature(feature2);
        }
        System.out.printf("New layer writes features: %s\
", layer1.GetFeatureCount());
        //The Layer obtained through ExecuteSQL must be destroyed using ReleaseResultSet before destroying the data source.
        ds.ReleaseResultSet(layerSQL);
        outDataSource.SyncToDisk();
        System.out.printf("Data fusion successful! File name: %s\
", outDataSource.GetName());

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

    }

}

Normal output:

Start blending data! Input file: E:\grids.shp
The content of the SQL statement is: SELECT ST_Union(GEOMETRY) AS GEOMETRY,grade,count(*) as count FROM grids group by grade
SQL statement executed successfully! Number of rows of data returned: 3
After fusion, there are 3 elements. After dividing non-adjacent elements, there are 4 elements.
Attribute field created successfully! Number of fields: 2
New layer writes features: 4
Fusion data successful! File name: E:\grids_merge.shp

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeHomepageOverview 138,710 people are learning the system