Core Java

Exploring Geometry Operations with JTS

The Java Topology Suite (JTS) is an open-source geometry engine written in Java. It provides a comprehensive API for modeling, analysing, and manipulating two-dimensional spatial data. JTS supports geometry operations such as buffering, intersection, union, and distance calculations with high precision, making it a core building block for geospatial systems, GIS applications, and spatial databases. This article explores some essential geospatial operations using JTS.

1. Introduction to JTS and WKT

JTS supports a wide range of geometry types, such as Point, LineString, Polygon, and collections like MultiPolygon or GeometryCollection. These geometries can be expressed using Well-Known Text (WKT), a standardized text format defined by the Open Geospatial Consortium (OGC) that provides a clear, human readable way to represent spatial objects and their coordinates.

For example, a Point can be written as POINT (12.4924 41.8902), a LineString as LINESTRING (12.4924 41.8902, 12.4964 41.9028), and a Polygon as POLYGON ((12.49 41.89, 12.50 41.89, 12.50 41.90, 12.49 41.90, 12.49 41.89)). These representations make it easier to define, store, and manipulate geometric data within Java applications using the Java Topology Suite.

2. Setting Up JTS

To begin, the JTS dependency should be added to the Maven project.

        <dependency>
            <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>1.20.0</version>
        </dependency>

Once added, we can start using JTS classes such as WKTReader, WKTWriter, and various geometry types (Point, Polygon, LineString).

2.1 Creating Geometries Using WKT

The Well-Known Text (WKT) format expresses geometries as plain text strings. JTS provides a WKTReader for parsing WKT into geometry objects and a WKTWriter for converting them back to text.

public class WKTGeometryExample {

    public static void main(String[] args) throws Exception {
        GeometryFactory geometryFactory = new GeometryFactory();
        WKTReader reader = new WKTReader(geometryFactory);

        // Create geometries using WKT strings
        Geometry point = reader.read("POINT (12.4924 41.8902)");
        Geometry lineString = reader.read("LINESTRING (12.4924 41.8902, 12.4964 41.9028)");
        Geometry polygon = reader.read("POLYGON ((12.49 41.89, 12.50 41.89, 12.50 41.90, 12.49 41.90, 12.49 41.89))");

        System.out.println("Point: " + point);
        System.out.println("LineString: " + lineString);
        System.out.println("Polygon: " + polygon);

        // Convert a geometry back to WKT
        WKTWriter writer = new WKTWriter();
        System.out.println("Polygon as WKT: " + writer.write(polygon));
    }
}

The WKTReader converts WKT strings into Geometry objects, and WKTWriter performs the reverse. This makes it easy to exchange geometries between systems, save them in databases, or log them for debugging.

3. Spatial Relationships and Predicates

JTS provides methods to determine spatial relationships between geometries. For instance, we can check whether one geometry contains, intersects, or touches another.

public class SpatialRelationshipExample extends JPanel{

    public static void main(String[] args) throws Exception {
        WKTReader reader = new WKTReader();

        Geometry polygon = reader.read("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))");
        Geometry pointInside = reader.read("POINT (5 5)");
        Geometry pointOutside = reader.read("POINT (15 5)");

        System.out.println("Contains inside point: " + polygon.contains(pointInside));
        System.out.println("Contains outside point: " + polygon.contains(pointOutside));
        System.out.println("Touches inside point: " + polygon.touches(pointInside));
    }
}

The program begins by creating an instance of the WKTReader class. Three geometries are defined: a square-shaped polygon with coordinates from (0,0) to (10,10), a point located inside the polygon at (5,5), and another point situated outside the polygon at (15,5).

The expressions polygon.contains(pointInside) and polygon.contains(pointOutside) evaluate whether each point lies within the boundaries of the polygon. When polygon.contains(pointInside) is executed, it returns true because the point (5,5) is clearly within the polygon’s coordinate range. In contrast, polygon.contains(pointOutside) returns false because (15,5) lies outside the polygon’s spatial extent. The final statement, polygon.touches(pointInside), checks if the point lies exactly on the polygon’s boundary. Since (5,5) is an interior point, not on the edge, this returns false.

When the code is executed, the console output will be:

Contains inside point: true
Contains outside point: false
Touches inside point: false

Spatial predicates like contains(), intersects(), touches(), and within() are fundamental for geospatial analysis. These operations are essential when building applications such as map overlays, route analysis, or geofencing.

4. Buffering and Proximity Analysis

The buffer operation expands or contracts a geometry by a specified distance. Buffers are used for defining influence zones, proximity alerts, or environmental impact areas.

public class BufferExample {

    public static void main(String[] args) throws Exception {
        WKTReader reader = new WKTReader();
        WKTWriter writer = new WKTWriter();

        Geometry point = reader.read("POINT (10 10)");
        Geometry buffer = point.buffer(5.0);

        System.out.println("Original Point: " + writer.write(point));
        System.out.println("Buffered Area: " + writer.write(buffer));
        System.out.println("Buffer Area Size: " + buffer.getArea());
    }
}

Here, a 5-unit buffer is created around the point (10, 10). The buffer() method generates a polygon that represents all points within 5 units of the original point, effectively forming a circle-like area around it. The resulting geometry is a polygon that approximates a circle. The getArea() method computes the surface area of the buffer region.

The output of the program looks like this:

Original Point: POINT (10 10)
Buffered Area: POLYGON ((15 10, 14.903926402016152 9.024548389919358, 14.619397662556434 8.08658283817455, 14.157348061512726 7.222148834901989, 13.535533905932738 6.464466094067262, 12.777851165098012 5.842651938487274, 11.91341716182545 5.380602337443566, 10.975451610080642 5.096073597983848, 10 5, 9.024548389919358 5.096073597983848, 8.086582838174552 5.380602337443566, 7.22214883490199 5.842651938487273, 6.464466094067262 6.464466094067262, 5.842651938487274 7.222148834901989, 5.380602337443566 8.08658283817455, 5.096073597983848 9.024548389919357, 5 10, 5.096073597983848 10.975451610080642, 5.380602337443566 11.913417161825448, 5.842651938487273 12.77785116509801, 6.464466094067261 13.535533905932738, 7.222148834901989 14.157348061512726, 8.086582838174548 14.619397662556432, 9.024548389919357 14.90392640201615, 10 15, 10.975451610080642 14.903926402016152, 11.91341716182545 14.619397662556434, 12.77785116509801 14.157348061512728, 13.535533905932738 13.535533905932738, 14.157348061512726 12.77785116509801, 14.619397662556432 11.913417161825452, 14.90392640201615 10.975451610080643, 15 10))
Buffer Area Size: 78.03612880645132

Here, the Buffered Area output is a polygon described in WKT format, showing a series of coordinate pairs that approximate a circle. The Buffer Area Size value (78.03612880645132) corresponds to the area of the buffered region. This type of operation is often used for proximity searches, such as finding nearby restaurants within a specified radius from a user’s location.

5. Calculating Distance Between Geometries

The distance function computes the shortest path between two geometries. It’s often used for measuring proximity in meters, kilometres, or projected units, depending on your coordinate system.

public class DistanceExample {

    public static void main(String[] args) throws Exception {
        WKTReader reader = new WKTReader();

        Geometry p1 = reader.read("POINT (0 0)");
        Geometry p2 = reader.read("POINT (3 4)");

        double distance = p1.distance(p2);
        System.out.println("Distance between points: " + distance);
    }
}

This code calculates the geometric distance between two points in a two-dimensional coordinate space. It defines two points: the first point POINT (0 0) represents the origin, and the second point POINT (3 4) represents another location positioned 3 units along the x-axis and 4 units along the y-axis.

When the program runs, it produces the following output:

Distance between points: 5.0

This value represents the straight-line distance between the two points on a plane. The result indicates that the two points are 5 kilometres apart.

6. Intersection and Union Operations

JTS supports topological operations that modify geometries based on their relationships. Common ones include intersection(), union(), and difference().

public class IntersectionExample {

    public static void main(String[] args) throws Exception {
        WKTReader reader = new WKTReader();
        WKTWriter writer = new WKTWriter();

        Geometry polygonA = reader.read("POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))");
        Geometry polygonB = reader.read("POLYGON ((3 3, 8 3, 8 8, 3 8, 3 3))");

        Geometry intersection = polygonA.intersection(polygonB);
        Geometry union = polygonA.union(polygonB);

        System.out.println("Intersection: " + writer.write(intersection));
        System.out.println("Union: " + writer.write(union));
    }
}

This code demonstrates the intersection and union of two polygon geometries. The first polygon, polygonA, covers the area with corners at coordinates (0,0), (5,0), (5,5), and (0,5), forming a square positioned in the lower-left region of the coordinate grid. The second polygon, polygonB, spans from (3,3) to (8,8), forming another square that partially overlaps the first one in the upper-right corner.

The intersection() method identifies the geometric area that the two polygons share, that is, the region where they overlap. The overlapping area is a smaller square defined by the coordinates (3,3), (5,3), (5,5), and (3,5). The union() method, on the other hand, combines both polygons into a single geometry that encompasses all the space covered by either polygon. In this case, the result is a larger polygon that represents the merged extent of the two overlapping squares.

When this program runs, the output will look similar to the following:

Intersection: POLYGON ((3 5, 5 5, 5 3, 3 3, 3 5))
Union: POLYGON ((5 3, 5 0, 0 0, 0 5, 3 5, 3 8, 8 8, 8 3, 5 3))

The intersection output shows the coordinates of the overlapping square, confirming that both polygons share the area between (3,3) and (5,5). The union output lists the outer boundary coordinates of the combined shape, which covers the full extent of both squares.

Difference: Subtract One Shape From Another

The difference operation subtracts one geometry from another, returning the part of the first geometry that does not overlap with the second. It’s often used for removing overlapping areas or cutting regions out of a base shape.

public class DifferenceExample {

    public static void main(String[] args) throws Exception {
        WKTReader reader = new WKTReader();
        WKTWriter writer = new WKTWriter();

        Geometry basePolygon = reader.read("POLYGON ((0 0, 8 0, 8 8, 0 8, 0 0))");
        Geometry cutoutPolygon = reader.read("POLYGON ((3 3, 6 3, 6 6, 3 6, 3 3))");

        Geometry difference = basePolygon.difference(cutoutPolygon);

        System.out.println("Base Polygon: " + writer.write(basePolygon));
        System.out.println("Cutout Polygon: " + writer.write(cutoutPolygon));
        System.out.println("Resulting Difference: " + writer.write(difference));
        System.out.println("Remaining Area: " + difference.getArea());
    }
}

The example begins by creating two polygons. The first polygon, basePolygon, defines a large square with coordinates (0,0), (8,0), (8,8), and (0,8), representing an 8×8 unit square. The second polygon, cutoutPolygon, defines a smaller square located inside the first one, with coordinates (3,3), (6,3), (6,6), and (3,6). This smaller polygon represents the section that will be removed or cut out from the base polygon.

The difference() method is then called on the basePolygon with cutoutPolygon as the argument. This operation computes the portion of the base polygon that does not overlap with the second polygon, effectively subtracting the overlapping region. When the program runs, it produces an output similar to the following:

Base Polygon: POLYGON ((0 0, 8 0, 8 8, 0 8, 0 0))
Cutout Polygon: POLYGON ((3 3, 6 3, 6 6, 3 6, 3 3))
Resulting Difference: POLYGON ((0 0, 0 8, 8 8, 8 0, 0 0), (3 3, 6 3, 6 6, 3 6, 3 3))
Remaining Area: 55.0

The Resulting Difference output represents a polygon with an outer boundary (the base square) and an inner boundary (the cutout). The inner coordinates define the hole, which is the part that has been removed. The remaining area value (55.0) is the total area of the base square minus the area of the removed smaller square.

7. Conclusion

In this article, we explored several core geometric operations available in the Java Topology Suite (JTS), demonstrating how they can be used to model and analyse spatial relationships between geometries. Through examples such as containment, buffering, distance calculation, intersection, and difference operations, we saw how JTS provides an API for performing spatial analysis with minimal code.

8. Download the Source Code

This article covered JTS geospatial operations using the Java Topology Suite.

Download
You can download the full source code of this example here: jts geospatial operations

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button