JavaGeoTools-Geometry CRS Tutorial

Table of Contents

    • foreword
    • CRS Lab Application
    • expand
      • Geometry

Foreword

The main content of this chapter is the transformation of the projected graphics coordinate system

CRS Lab Application

First, import the required dependencies

 <dependencies>
        <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-epsg-hsql</artifactId>
            <version>${geotools.version}</version>
        </dependency>
    </dependencies>
  <repositories>
    <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>
  </repositories>

Then create our CRSLab class

public class CRSLab {<!-- -->

    private File sourceFile;
    private SimpleFeatureSource featureSource;
    private MapContent map;

    public static void main(String[] args) throws Exception {<!-- -->
        CRSLab lab = new CRSLab();
        lab.displayShapefile();
    }

Here, the CRSLab class is new in the main, and the displayShapefile method is executed. It can be seen from the name that it is a method for displaying shapefiles. See below for the specific implementation

private void displayShapefile() throws Exception {<!-- -->
        sourceFile = JFileDataStoreChooser. showOpenFile("shp", null);
        if (sourceFile == null) {<!-- -->
            return;
        }
        FileDataStore store = FileDataStoreFinder.getDataStore(sourceFile);
        featureSource = store. getFeatureSource();

        // Create a map context and add our shapefile to it
        map = new MapContent();
        Style style = SLD.createSimpleStyle(featureSource.getSchema());
        Layer layer = new FeatureLayer(featureSource, style);
        map.layers().add(layer);

        // Create a JMapFrame with custom toolbar buttons
        JMapFrame mapFrame = new JMapFrame(map);
        mapFrame.enableToolBar(true);
        mapFrame.enableStatusBar(true);

        JToolBar toolbar = mapFrame. getToolBar();
        toolbar. addSeparator();
        toolbar.add(new JButton(new ValidateGeometryAction()));
        toolbar.add(new JButton(new ExportShapefileAction()));

        // Display the map frame. When it is closed the application will exit
        mapFrame. setSize(800, 600);
        mapFrame. setVisible(true);
    }

In the displayShapefile method, first use the file selector JFileDataStoreChooser.showOpenFile("shp", null); to select the shapefile. Then create a map container new MapContent(); and a map frame new JMapFrame(map); to display the loaded shapefile. There is nothing to say about this step, the key point is below, we added two buttons to the ToolBar of the map frame

 JToolBar toolbar = mapFrame.getToolBar();
        toolbar. addSeparator();
        toolbar.add(new JButton(new ValidateGeometryAction()));
        toolbar.add(new JButton(new ExportShapefileAction()));

And define two classes new ValidateGeometryAction() and new ExportShapefileAction() for the two buttons as the execution content after the button is clicked, the key of this article is also in these two between classes.
first look at the first

class ValidateGeometryAction extends SafeAction {<!-- -->
        ValidateGeometryAction() {<!-- -->
            super("Validate geometry");
            putValue(Action. SHORT_DESCRIPTION, "Check each geometry");
        }

        public void action(ActionEvent e) throws Throwable {<!-- -->
            int numInvalid = validateFeatureGeometry(null);
            String msg;
            if (numInvalid == 0) {<!-- -->
                msg = "All feature geometries are valid";
            } else {<!-- -->
                msg = "Invalid geometries: " + numInvalid;
            }
            JOptionPane.showMessageDialog(
                    null, msg, "Geometry results", JOptionPane. INFORMATION_MESSAGE);
        }
    }

You can see that this class inherits the SafeAction class
By browsing the source code, we can see that SafeAction is a subclass of AbstractAction, which belongs to its “safe version”, and it will record all error messages. And AbstractAction is the implementation of the JFC Action interface, which is the interface for execution (it is a bit difficult to describe here, if you are interested, you can check the source code, if you are not interested, you only need to know that we inherit SafeAction and rewrite its action method to realize the function of the button ok)
Another method validateFeatureGeometry(null) is called in the action method
The details of the method are as follows

 private int validateFeatureGeometry(ProgressListener progress) throws Exception {<!-- -->
        final SimpleFeatureCollection featureCollection = featureSource. getFeatures();

        // Rather than use an iterator, create a FeatureVisitor to check each feature
        class ValidationVisitor implements FeatureVisitor {<!-- -->
            public int numInvalidGeometries = 0;

            public void visit(Feature f) {<!-- -->
                SimpleFeature feature = (SimpleFeature) f;
                Geometry geom = (Geometry) feature. getDefaultGeometry();
                if (geom != null & amp; & amp; !geom.isValid()) {<!-- -->
                    numInvalidGeometries++;
                    System.out.println("Invalid Geoemtry: " + feature.getID());
                }
            }
        }

        ValidationVisitor visitor = new ValidationVisitor();

        // Pass visitor and the progress bar to feature collection
        featureCollection. accepts(visitor, progress);
        return visitor.numInvalidGeometries;
    }

Its role is to verify whether the feature graphics in the shapefile are legal, and return the number of legal features in the above read features. The function of this button is to verify the number of legal features in the imported shapefile.
Look at the class of the second button

 class ExportShapefileAction extends SafeAction {<!-- -->
        ExportShapefileAction() {<!-- -->
            super("Export...");
            putValue(Action.SHORT_DESCRIPTION, "Export using current crs");
        }

        public void action(ActionEvent e) throws Throwable {<!-- -->
            exportToShapefile();
        }
    }

This class calls the method exportToShapefile()

 private void exportToShapefile() throws Exception {<!-- -->
        SimpleFeatureType schema = featureSource. getSchema();
        JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
        chooser.setDialogTitle("Save reprojected shapefile");
        chooser.setSaveFile(sourceFile);
        int returnVal = chooser. showSaveDialog(null);
        if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {<!-- -->
            return;
        }
        File file = chooser. getSelectedFile();
        if (file. equals(sourceFile)) {<!-- -->
            JOptionPane.showMessageDialog(null, "Cannot replace " + file);
            return;
        }
 CoordinateReferenceSystem dataCRS = schema. getCoordinateReferenceSystem();
        CoordinateReferenceSystem worldCRS = map.getCoordinateReferenceSystem();
        boolean lenient = true; // allow for some error due to different datums
        MathTransform transform = CRS.findMathTransform(dataCRS, worldCRS, lenient);
\t\t
SimpleFeatureCollection featureCollection = featureSource. getFeatures();
 Transaction transaction = new DefaultTransaction("Reproject");
        try (FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
                        dataStore.getFeatureWriterAppend(createdName, transaction);
                SimpleFeatureIterator iterator = featureCollection. features()) {<!-- -->
            while (iterator.hasNext()) {<!-- -->
                // copy the contents of each feature and transform the geometry
                SimpleFeature feature = iterator. next();
                SimpleFeature copy = writer. next();
                copy.setAttributes(feature.getAttributes());

                Geometry geometry = (Geometry) feature. getDefaultGeometry();
                Geometry geometry2 = JTS. transform(geometry, transform);

                copy.setDefaultGeometry(geometry2);
                writer. write();
            }
            transaction.commit();
            JOptionPane.showMessageDialog(null, "Export to shapefile complete");
        } catch (Exception problem) {<!-- -->
            problem. printStackTrace();
            transaction. rollback();
            JOptionPane.showMessageDialog(null, "Export to shapefile failed");
        } finally {<!-- -->
            transaction. close();
        }
    }

It can be seen from the name that it is a class that exports shapefiles, or look at the functions line by line
The first is to choose the location to save the file

 SimpleFeatureType schema = featureSource. getSchema();
        JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
        chooser.setDialogTitle("Save reprojected shapefile");
        chooser.setSaveFile(sourceFile);
        int returnVal = chooser. showSaveDialog(null);
        if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {<!-- -->
            return;
        }
        File file = chooser. getSelectedFile();
        if (file. equals(sourceFile)) {<!-- -->
            JOptionPane.showMessageDialog(null, "Cannot replace " + file);
            return;
        }

Then convert the coordinates

 CoordinateReferenceSystem dataCRS = schema. getCoordinateReferenceSystem();
        CoordinateReferenceSystem worldCRS = map.getCoordinateReferenceSystem();
        boolean lenient = true; // allow for some error due to different datums
        MathTransform transform = CRS.findMathTransform(dataCRS, worldCRS, lenient);

Here, the coordinate system of the source shapefile is first obtained, and then the left side of the map container is obtained, and then the mathematical method transform between the two is established.
The following creates a feature type for the exported shapefile

 DataStoreFactorySpi factory = new ShapefileDataStoreFactory();
        Map<String, Serializable> create = new HashMap<>();
        create.put("url", file.toURI().toURL());
        create. put("create spatial index", Boolean. TRUE);
        DataStore dataStore = factory. createNewDataStore(create);
        SimpleFeatureType featureType = SimpleFeatureTypeBuilder. retype(schema, worldCRS);
        dataStore. createSchema(featureType);

        // Get the name of the new Shapefile, which will be used to open the FeatureWriter
        String createdName = dataStore. getTypeNames()[0];

Finally, create a transaction to add all the elements of the source shapefile to the new file after converting the coordinate system

 SimpleFeatureCollection featureCollection = featureSource.getFeatures();
         Transaction transaction = new DefaultTransaction("Reproject");
        try (FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
                        dataStore.getFeatureWriterAppend(createdName, transaction);
                SimpleFeatureIterator iterator = featureCollection. features()) {<!-- -->
            while (iterator.hasNext()) {<!-- -->
                // copy the contents of each feature and transform the geometry
                SimpleFeature feature = iterator. next();
                SimpleFeature copy = writer. next();
                copy.setAttributes(feature.getAttributes());

                Geometry geometry = (Geometry) feature. getDefaultGeometry();
                Geometry geometry2 = JTS. transform(geometry, transform);

                copy.setDefaultGeometry(geometry2);
                writer. write();
            }
            transaction.commit();
            JOptionPane.showMessageDialog(null, "Export to shapefile complete");
        } catch (Exception problem) {<!-- -->
            problem. printStackTrace();
            transaction. rollback();
            JOptionPane.showMessageDialog(null, "Export to shapefile failed");
        } finally {<!-- -->
            transaction. close();
        }

It can be seen that the transform method of JTS is used here and the source geometry and transformed transform are passed in to transform the coordinate system.
Finally, run the main method.

Expand

Geometry

Geometry is actually the shape of GIS, which is the geometric shape of elements.
Here are a few typical Geometries and how they are created
Point

GeometryFactory geometryFactory = JTSFactoryFinder. getGeometryFactory(null);

WKTReader reader = new WKTReader(geometryFactory);
Point point = (Point) reader. read("POINT (1 1)");

Line

GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);

WKTReader reader = new WKTReader(geometryFactory);
LineString line = (LineString) reader.read("LINESTRING(0 2, 2 0, 8 6)");