Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using GeoJSON With Spring Data for MongoDB and Spring Boot

DZone's Guide to

Using GeoJSON With Spring Data for MongoDB and Spring Boot

· Java Zone
Free Resource

Are you joining the containers revolution? Start leveraging container management using Platform9's ultimate guide to Kubernetes deployment.

In my previous articles I compared 4 frameworks commonly used in communicating with MongoDB from the JVM and found out that in that use-case, Spring Data for MongoDB was the easiest solution. However I did make the remark that it doesn’t use the GeoJSON format to store geolocation coordinates and geometries.

I tried to add GeoJSON support before, but couldn’t get the conversion to work propertly. But after some extensive searching I found out that the reason for it not working was my use of Spring Boot: its autoconfiguration for MongoDB does not support custom conversion out of the box. Luckily, the solution was simple: provide an extra configuration that extends from AbstractMongoConfiguration and import that in the Boot application. In that configuration you can override the customConversions() and add your converters.

When you compare the geo classes in Spring Data and GeoJSON, I noticed that only a subset of GeoJSON geometries can be mapped on Spring Data geo classes: Point and Polygon. Spring Boot does not support LineString, MultiLineString, MultiPolygon or MultiPoint. However, in your mapped domain classes, you won’t use these normally. Creating a converter that adheres to the GeoJSON format is quite straightforward.

import com.mongodb.BasicDBObject
import com.mongodb.DBObject
import org.springframework.core.convert.converter.Converter
import org.springframework.data.convert.ReadingConverter
import org.springframework.data.convert.WritingConverter
import org.springframework.data.geo.Point
import org.springframework.data.geo.Polygon
final class GeoJsonConverters {
    static List<Converter<?, ?>> getConvertersToRegister() {
        return [
                GeoJsonDBObjectToPointConverter.INSTANCE,
                GeoJsonDBObjectToPolygonConverter.INSTANCE,
                GeoJsonPointToDBObjectConverter.INSTANCE,
                GeoJsonPolygonToDBObjectConverter.INSTANCE
        ]
    }
    @WritingConverter
    static enum GeoJsonPointToDBObjectConverter implements Converter<Point, DBObject> {
        INSTANCE;
        @Override
        DBObject convert(Point source) {
            return new BasicDBObject([type: 'Point', coordinates: [source.x, source.y]])
        }
    }
    @ReadingConverter
    static enum GeoJsonDBObjectToPointConverter implements Converter<DBObject, Point> {
        INSTANCE;
        @Override
        Point convert(DBObject source) {
            def coordinates = source.coordinates as double[]
            return new Point(coordinates[0], coordinates[1])
        }
    }
    @WritingConverter
    static enum GeoJsonPolygonToDBObjectConverter implements Converter<Polygon, DBObject> {
        INSTANCE;
        @Override
        DBObject convert(Polygon source) {
            def coordinates = source.points.collect {
                [it.x, it.y]
            }
            return new BasicDBObject([type: 'Polygon', coordinates: coordinates])
        }
    }
    @ReadingConverter
    static enum GeoJsonDBObjectToPolygonConverter implements Converter<DBObject, Polygon> {
        INSTANCE;
        @Override
        Polygon convert(DBObject source) {
            def coordinates = source.coordinates as double[]
            return new Point(coordinates[0], coordinates[1])
        }
    }
}

To add those converters to the Spring context, you’ll have to override some methods in your MongoDB spring configuration class.

import com.mongodb.Mongo
import org.springframework.beans.factory.annotation.*
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.*
import org.springframework.data.mongodb.config.AbstractMongoConfiguration
import org.springframework.data.mongodb.core.convert.*
@EnableAutoConfiguration
@ComponentScan
@Configuration
@Import([MongoComparisonMongoConfiguration])
class MongoComparison
{
    static void main(String[] args) {
        SpringApplication.run(MongoComparison, args);
    }
}
@Configuration
class MongoComparisonMongoConfiguration extends AbstractMongoConfiguration {
    @Autowired
    Mongo mongo;
    @Value("\${spring.data.mongodb.database}")
    String databaseName;
    @Override
    protected String getDatabaseName() {
        return databaseName
    }
    @Override
    Mongo mongo() throws Exception {
        return mongo
    }
    @Override
    CustomConversions customConversions() {
        def customConverters = []
        customConverters << GeoJsonConverters.convertersToRegister
        return new CustomConversions(customConverters.flatten())
    }
}

As Spring Boot already provides the configuration of the Mongo instance and the name of the database, we can reuse these in the MongoDB configuration class. The custom conversions take preference over the existing ones for Point and Polygon.

I’ll be writing a library this weekend to add support for all GeoJSON geometries in Spring Data for MongoDB. However, I already noticed it’ll be very hard to provide support for those in generated query methods in repositories, but with annotated queries being possible, I don’t think this will be a big issue but we’ll see.

Using Containers? Read our Kubernetes Comparison eBook to learn the positives and negatives of Kubernetes, Mesos, Docker Swarm and EC2 Container Services.

Topics:

Published at DZone with permission of Lieven Doclo. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}