Skip to content

nramc/geojson4j

Repository files navigation

geojson4j

A lightweight, RFC 7946-compliant GeoJSON library for Java

CI Quality Gate Coverage Maven Central License Java

Getting Started · Usage · API Reference · Contributing


What is geojson4j?

geojson4j is a Java domain library for working with GeoJSON (RFC 7946) data. It lets you create, parse, validate, and serialize all standard GeoJSON types using a fluent, type-safe API built on top of Jackson.

Why geojson4j?

  • RFC 7946 compliant — full specification coverage
  • 🔒 Immutable domain objects — thread-safe by design
  • Eager validation — factory methods throw on invalid data; constructors stay deserialization-friendly
  • 🔌 Jackson-native — drop-in serialization/deserialization with @JsonCreator / @JsonSubTypes polymorphism
  • 🧪 Well-tested — serialization, deserialization, invalid states, and validation error keys all covered

Supported GeoJSON Types

GeoJSON Type Java Class RFC 7946 Section
Point Point §3.1.2
LineString LineString §3.1.4
Polygon Polygon §3.1.6
MultiPoint MultiPoint §3.1.3
MultiLineString MultiLineString §3.1.5
MultiPolygon MultiPolygon §3.1.7
GeometryCollection GeometryCollection §3.1.8
Feature Feature §3.2
FeatureCollection FeatureCollection §3.3

Installation

Requirements: Java 21+, Maven or Gradle

Maven

<dependency>
    <groupId>io.github.nramc</groupId>
    <artifactId>geojson4j</artifactId>
    <version>${geojson4j.version}</version>
</dependency>

Gradle

implementation 'io.github.nramc:geojson4j:1.0.17'

Check Maven Central for the latest version.


Usage

Creating GeoJSON Objects

Use the static of(...) factory methods. They validate eagerly and throw GeoJsonValidationException if the data is invalid.

// Point — longitude, latitude
Point point = Point.of(102.0, 0.5);

// Point with altitude
Point point3d = Point.of(102.0, 0.5, 256.0);

// LineString — varargs of Position
LineString line = LineString.of(
        Position.of(102.0, 0.0),
        Position.of(103.0, 1.0),
        Position.of(104.0, 0.0)
);

// Polygon — exterior ring must have ≥ 4 positions; first == last (closed)
// PolygonCoordinates.of() accepts one or more List<Position> rings (first = exterior)
List<Position> exterior = List.of(
        Position.of(100.0, 0.0),
        Position.of(101.0, 0.0),
        Position.of(101.0, 1.0),
        Position.of(100.0, 1.0),
        Position.of(100.0, 0.0)   // close the ring
);
Polygon polygon = Polygon.of(exterior);

// Feature — id (nullable), geometry, properties
Map<String, Serializable> props = new HashMap<>();
props.put("name", "My Location");
props.put("category", "landmark");
Feature feature = Feature.of("location-1", point, props);

// FeatureCollection
FeatureCollection collection = FeatureCollection.of(List.of(feature));

Serialization (Object → JSON)

ObjectMapper mapper = new ObjectMapper();

Point point = Point.of(102.0, 0.5);
String json = mapper.writeValueAsString(point);
// {"type":"Point","coordinates":[102.0,0.5]}

Deserialization (JSON → Object)

Deserialize to a concrete type:

String json = """
        { "type": "Point", "coordinates": [102.0, 0.5] }
        """;

ObjectMapper mapper = new ObjectMapper();
Point point = mapper.readValue(json, Point.class);

double lon = point.getCoordinates().getLongitude(); // 102.0
double lat = point.getCoordinates().getLatitude();  // 0.5

Deserialize to the base type and let Jackson resolve the subtype via the type field:

GeoJson geoJson = mapper.readValue(json, GeoJson.class);   // polymorphic root
Geometry geometry = mapper.readValue(json, Geometry.class);  // geometry-only polymorphic

Validation

Constructors (used during deserialization) do not validate eagerly — this lets you inspect partially-populated objects. Use validate() for detailed error information or isValid() for a quick check.

// Quick boolean check
GeoJson geoJson = mapper.readValue(json, GeoJson.class);
boolean valid = geoJson.isValid();

// Detailed validation result
ValidationResult result = geoJson.validate();

if (result.hasErrors()) {
    result.getErrors().forEach(error ->
        System.out.printf("Field: %-30s Key: %-40s Message: %s%n",
            error.getField(), error.getKey(), error.getMessage())
    );
}

Stable validation error keys you can rely on in code:

Key Meaning
type.invalid type field is missing or wrong
coordinates.invalid.empty coordinates is null or empty
coordinates.latitude.invalid latitude is out of the valid range
coordinates.longitude.invalid longitude is out of the valid range
coordinates.invalid.minimum.positions not enough positions (e.g., polygon ring < 4)
coordinates.invalid.ring.closed polygon ring is not closed (first ≠ last)
geometries.invalid.nested.geometry GeometryCollection contains another one

Fail-fast with factory methods

try {
    // Latitude 999 is out of range → throws immediately
    Point invalid = Point.of(102.0, 999.0);
} catch (GeoJsonValidationException ex) {
    // handle validation failure
}

API Reference

Class Hierarchy

GeoJson (sealed)
├── Geometry (sealed)
│   ├── Point
│   ├── LineString
│   ├── Polygon
│   ├── MultiPoint
│   ├── MultiLineString
│   ├── MultiPolygon
│   └── GeometryCollection
├── Feature
└── FeatureCollection

Key Types

Type Description
GeoJson Sealed root; Jackson polymorphism resolved via type
Geometry Sealed base for all geometry types
Position Longitude / latitude / optional altitude coordinate tuple
PolygonCoordinates Enforces ring rules (≥ 4 positions, closed)
Validatable Interface providing validate(), isValid(), hasErrors()
ValidationResult Holds a set of ValidationError instances
GeoJsonValidationException Thrown by factory methods on invalid input

Examples

A runnable Spring MVC example is available in examples/spring-mvc-example. It demonstrates:

  • Echo endpoint — round-trip serialization of any GeoJson object
  • Validation endpoint — returns ValidationResult as JSON
  • CRUD endpoint — persists and retrieves GeoJson using JPA + H2

Build & Development

# Run tests only
mvn test

# Full build including Checkstyle
mvn verify

# With code-coverage report
mvn -Pcoverage verify

# Apply OpenRewrite recipes (license headers, static-analysis fixes …)
mvn -Popen-rewrite process-sources

# Dry-run OpenRewrite (check without applying)
mvn -Popen-rewrite prepare-package

Code style is enforced by Checkstyle (config/checkstyle/geojson4j_checks.xml):

  • Google-style base
  • 160-character line limit
  • No wildcard imports

Contributing

Contributions are very welcome! Here's how to get started:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Commit your changes following the Conventional Commits format
  4. Push the branch: git push origin feature/my-feature
  5. Open a Pull Request — the CI pipeline will run automatically

Please open an issue on GitHub Issues to report bugs or request features before starting a large change.

Adding a New GeoJSON Type

Follow the pattern established by existing types:

  1. Add the type constant in GeoJsonType.java
  2. Register @JsonSubTypes entries in both GeoJson and Geometry
  3. Implement the class: no-arg constructor + @JsonCreator constructor + of(...) factory + validate() + equals/hashCode/toString
  4. Add tests covering serialization, base-type deserialization, invalid states, and eager-validation exceptions

License

Distributed under the Apache License 2.0. See LICENSE for full text.


Acknowledgements

Tool Purpose
Jackson JSON serialization / deserialization
SonarCloud Static analysis & quality gate
OpenRewrite Automated code refactoring
Renovate Automated dependency updates

Contact

Ramachandran Nellaiyappan

GitHub LinkedIn Email


⭐ If you find this project useful, please give it a star — it helps a lot!

About

A Java library for working with GeoJSON data, compliant with RFC 7946. GeoJSON4j provides easy-to-use utilities for creating, parsing, and serializing GeoJSON objects, such as Points, LineStrings, Polygons, and FeatureCollections, while fully adhering to the official GeoJSON specification (RFC 7946).

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages