eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

Geospatial capabilities are essential in today’s software landscape, whether we’re building delivery apps, drone zones, fleet tracking, or geographic analytics. Java Topology Suite (JTS) is a geometry engine written in Java that provides a rich API for modeling and processing 2D spatial data.

In this tutorial, we’ll explore the essentials of JTS through code examples and real-world use cases.

2. Set Up JTS

To start using JTS in our Java project, we need to add the Maven dependency:

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

3. Creating Geometries Using WKT

Before performing geospatial operations, we must first define our spatial data. JTS supports Well-Known Text (WKT), a standardized format for describing geometry types, such as points, lines, and polygons.

Here is the WKT geometry syntax:

  • POINT (x, y): a single coordinate, e.g., POINT (10, 20)
  • POLYGON ((x1 y1, x2 y2, …, xN yN)): a closed polygon, first and last points must be the same

Let’s create a GeometryFactoryUtil class and a readWKT() method to read geometry in the WKT format:

public class GeometryFactoryUtil {
    public static Geometry readWKT(String wkt) throws Exception {
        WKTReader reader = new WKTReader();
        return reader.read(wkt);
    }
}

3.1. Containment: Is a Point Inside a Polygon?

The contains() method checks whether one geometry is completely within another. This is frequently used in geofencing, zoning, and tracking applications.

First, let’s create a new class called JTSOperationUtils and then create our method to check if a point is inside the polygon:

public class JTSOperationUtils {
    private static final Logger log = LoggerFactory.getLogger(JTSOperationUtils.class);

    public static boolean checkContainment(Geometry point, Geometry polygon) {
        boolean isInside = polygon.contains(point);
        log.info("Is the point inside polygon? {}", isInside);
        return isInside;
    }
}

Now let’s create our test to check if a point is inside the polygon:

@Test
public void givenPolygon2D_whenContainPoint_thenContainmentIsTrue() throws Exception {
    Geometry point = GeometryFactoryUtil.readWKT("POINT (10 20)");
    Geometry polygon = GeometryFactoryUtil.readWKT("POLYGON ((0 0, 0 40, 40 40, 40 0, 0 0))");
    Assert.assertTrue(JTSOperationUtils.checkContainment(point, polygon));
}

The point (10, 20) lies within a square polygon that spans from (0, 0) to (40, 40). The contains() method returns true when the point is entirely inside the polygon boundaries.

3.2. Intersection: Do Two Geometries Overlap?

The intersects() method checks if two geometries touch or overlap. We can also use the intersection() method to determine the overlapping region.

Let’s add a new method called checkIntersect() to our JTSOperationUtils to check if two geometries intersect each other and to determine the overlapping region:

public static boolean checkIntersect(Geometry rectangle1, Geometry rectangle2) {
    boolean intersect = rectangle1.intersects(rectangle2);
    Geometry overlap = rectangle1.intersection(rectangle2);

    log.info("Do both rectangle intersect? {}", intersect);
    log.info("Overlapping Area: {}", overlap);
    return intersect;
}

The two rectangles intersect partially. The intersects() method returns true, and the intersection() method gives the geometry of the overlapping area.

Now let’s add a new test to check if two geometries intersect each other:

@Test
public void givenRectangle1_whenIntersectWithRectangle2_thenIntersectionIsTrue() throws Exception {
    Geometry rectangle1 = GeometryFactoryUtil.readWKT("POLYGON ((10 10, 10 30, 30 30, 30 10, 10 10))");
    Geometry rectangle2 = GeometryFactoryUtil.readWKT("POLYGON ((20 20, 20 40, 40 40, 40 20, 20 20))");
    Assert.assertTrue(JTSOperationUtils.checkIntersect(rectangle1, rectangle2));
}

The checkIntersect() function checks whether two geometries (in this case, rectangles) overlap by using the intersects method. The unit test confirms this behavior by defining two rectangles that partially overlap and asserting that checkIntersect() returns true, proving the intersection detection works correctly.

3.3. Buffer: Create a Radius Around a Point

The buffer() method creates a circular or polygonal area around a geometry. It’s commonly used for proximity detection or safe zone modeling.

We’ll create a new method called getBuffer() in our utils class to handle adding a buffer to our geometry point:

public static Geometry getBuffer(Geometry point, integer intBuffer) {
    Geometry buffer = point.buffer(intBuffer);
    log.info("Buffer Geometry: {}", buffer);
    return buffer;
}

This method creates a buffer zone around a specified geometry (typically a point in this case). In geospatial processing, a buffer represents an area surrounding a geometry at a specified distance. It’s commonly used for proximity analysis, such as finding all features within a certain distance from a location.

Now let’s add a new test to assert a new buffer geometry contains our original geometry:

@Test
public void givenPoint_whenAddedBuffer_thenPointIsInsideTheBuffer() throws Exception {
    Geometry point = GeometryFactoryUtil.readWKT("POINT (10 10)");
    Geometry bufferArea = JTSOperationUtils.getBuffer(point, 5);
    Assert.assertTrue(JTSOperationUtils.checkContainment(point, bufferArea));
}

The getBuffer() function creates a buffer area around a given geometry (like a point) at a specified distance, returning a polygon that represents the surrounding zone. The unit test verifies this by creating a point (10,10), generating a buffer of 5 units around it, and checking that the point is indeed inside the resulting buffer, ensuring the buffer operation works correctly.

3.4. Distance: How Far Apart Are Two Points?

The distance() method calculates the Euclidean Distance between two geometries. This is useful in nearest location searches and alerts.

We’ll name this method getDistance() in our utils class:

public static double getDistance(Geometry point1, Geometry point2) {
    double distance = point1.distance(point2);
    log.info("Distance: {}",distance);
    return distance;
}

This method is designed to calculate the distance between two geometric objects, typically points, using the JTS Geometry library. If both inputs are points, the result will be the Euclidean Distance. For other types of geometries, such as lines or polygons, it represents the minimum distance between their boundaries.

Now, let’s add a new test to assert that the distance we get from the distance() method is correct:

@Test
public void givenTwoPoints_whenGetDistanceBetween_thenGetTheDistance() throws Exception {
    Geometry point1 = GeometryFactoryUtil.readWKT("POINT (10 10)");
    Geometry point2 = GeometryFactoryUtil.readWKT("POINT (13 14)");
    double distance = JTSOperationUtils.getDistance(point1, point2);
    double expectedResult = 5.00;
    double delta = 0.00;
    Assert.assertEquals(expectedResult, distance, delta);
}

This test calculates the distance between the two points (10, 10) and (13, 14), which returns 5.0 units from the Euclidean calculator.

3.5. Union: Merge Two Polygons

The union() method combines two geometries into a single shape. This is useful for merging areas, such as land parcels or administrative zones.

We’ll add a new method called getUnion() in our utils class to combine 2 geometries:

public static Geometry getUnion(Geometry geometry1, Geometry geometry2) {
    Geometry union = geometry1.union(geometry2);
    log.info("Union Result: {}", union);
    return union;
}

This test is written to verify that the getUnion() method correctly combines two adjacent polygons into a single larger polygon:

@Test
public void givenTwoGeometries_whenGetUnionOfBoth_thenGetTheUnion() throws Exception {
    Geometry geometry1 = GeometryFactoryUtil.readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))");
    Geometry geometry2 = GeometryFactoryUtil.readWKT("POLYGON ((10 0, 10 10, 20 10, 20 0, 10 0))");

    Geometry union = JTSOperationUtils.getUnion(geometry1, geometry2);
    Geometry expectedResult = GeometryFactoryUtil.readWKT("POLYGON ((0 0, 0 10, 10 10, 20 10, 20 0, 10 0, 0 0))");
    Assert.assertEquals(expectedResult, union);
}

In this test, two square polygons are defined adjacent to each other, sharing a common edge. Two adjacent rectangles are combined into one larger polygon that spans from (0, 0) to (20, 10). The assertion confirms that the union operation produces the correct geometry, ensuring that the implementation works as intended.

3.6. Difference: Subtract One Shape From Another

The difference() method subtracts one geometry from another, for example, to remove restricted zones or masked areas.

Let’s define a new method getDifference() in our utils class:

public static Geometry getDifference(Geometry base, Geometry cut) {
    Geometry result = base.difference(cut);
    log.info("Resulting Geometry: {}", result);
    return result;
}

This method takes two geometries as input, where the first one acts as the base shape and the second one represents the shape to be cut out.

Let’s add a new unit test to check whether the getDifference() method can correctly subtract one rectangle from another when they overlap.:

@Test
public void givenBaseRectangle_whenAnotherRectangleOverlapping_thenGetTheDifferenceRectangle() throws Exception {
    Geometry base = GeometryFactoryUtil.readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))");
    Geometry cut = GeometryFactoryUtil.readWKT("POLYGON ((5 0, 5 10, 10 10, 10 0, 5 0))");

    Geometry result = JTSOperationUtils.getDifference(base, cut);
    Geometry expectedResult = GeometryFactoryUtil.readWKT("POLYGON ((0 0, 0 10, 5 10, 5 0, 0 0))");
    Assert.assertEquals(expectedResult, result);
}

In this test, the base rectangle spans from (0,0) to (10,10), while the second rectangle overlaps the right half of it from (5,0) to (10,10). The getDifference() method subtracts the overlapping area, leaving only the left half of the base rectangle, which stretches from (0,0) to (5,10). The test then checks if the output matches the expected remaining shape, ensuring that the difference operation works as intended.

3.7. Geometry Validation and Repair

Invalid geometries (e.g., self-intersecting polygons) can cause problems in spatial computations. JTS offers the isValid() method to check validity and the buffer(0) method to auto-correct them.

Let’s add a new method called validateAndRepair() to our utils class:

public static Geometry validateAndRepair(Geometry invalidGeo) throws Exception {
    boolean valid = invalidGeo.isValid();
    log.info("Is valid Geometry value? {}", valid);

    Geometry repaired = invalidGeo.buffer(0);
    log.info("Repaired Geometry: {}", repaired);
    return repaired;
}

The input polygon is self-intersecting. Applying the buffer(0) method corrects it by reconstructing a valid geometry from the invalid input. JTS was only able to extract a small, valid triangle from the original invalid bowtie shape, and only the part that formed a valid enclosed shape was kept.

Let’s add a new test to ensure the expected geometry after the buffer(0) method fix is the same as the actual result we got:

@Test
public void givenInvalidGeometryValue_whenValidated_thenGiveFixedResult() throws Exception {
    Geometry invalidGeo = GeometryFactoryUtil.readWKT("POLYGON ((0 0, 5 5, 5 0, 0 5, 0 0))");
    Geometry result = JTSOperationUtils.validateAndRepair(invalidGeo);

    Geometry expectedResult = GeometryFactoryUtil.readWKT("POLYGON ((2.5 2.5, 5 5, 5 0, 2.5 2.5))");
    Assert.assertEquals(expectedResult, result);
}

The original value of geometry that is invalid creates a self-intersection that JTS can’t cleanly break into two valid parts. So it conservatively keeps only the largest valid ring it can find, in this case, the triangle near the right side.

4. Conclusion

In this article, we learned that the Java Topology Suite (JTS) makes working with spatial data in Java intuitive and powerful. It supports advanced geometric modeling, validation, and spatial computations, all of which are essential for geospatial applications.

We now have hands-on knowledge of:

  • Constructing geometries with WKT
  • Performing spatial operations (contain, buffer, distance, union, etc.)
  • Handling invalid geometries
  • Applying these tools in real-world scenarios

Whether we’re building a mapping tool, a logistics engine, or a location-aware app, JTS equips us with the core tools to work spatially at scale and with precision.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)