Skip to content

Geospatial Persistence and Queries

xamry edited this page Nov 8, 2012 · 3 revisions

Kundera allows you to store and query on geographical data in NoSQL. (Currently only MongoDB is supported).

Different databases may have different ways and formats of storing geographical information. Kundera abstracts over this and provides a standard and cross-database interface for geographical database persistence and queries.

Since JPA doesn't include any GIS related specification, Kundera uses Simple Features Specification from Open Geospatial Consortium (OGC) which is the most popular industry standard for providing inter operable solutions that "geo-enable" the Web, wireless and location-based services and mainstream IT.

Kundera uses JTS Topology Suite, which conforms to the Simple Features Specification for SQL published by the Open GIS Consortium.

In this document, we are going to explain how to declare geolocation fields, specify geospatial indexes, perform CRUD and run Geospatial queries. MongoDB will be used as datastore.

This presentation is pretty helpful in understanding Geospatial support in MongoDB. A quick-start with writing geospatial queries in Java is a good starting point.

Declaring Geolocation Field and Specifying 2D index

All geolocation fields should have data-type com.impetus.kundera.gis.geometry.Point which has following signature:

public class Point extends com.vividsolutions.jts.geom.Point 
{     
}

Point class encapsulates x, y and z coordinates. (z is unused as only 2D indexes are supported). Geolocation fields are allowed in entity classes as well as Embeddable classes. An entity class called Person with geolocation field named currentLocation is shown below:

Person.java

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import com.impetus.kundera.gis.geometry.Point;
import com.impetus.kundera.index.Index;
import com.impetus.kundera.index.IndexCollection;

@Entity
@Table(name="PERSON_LOCATION", schema = "KunderaExamples@mongoTest")
@IndexCollection(columns = { @Index(name = "currentLocation", type = "GEO2D")})
public class Person
{
    @Id
    @Column(name = "PERSON_ID")
    private int personId;    
    
    @Column(name = "PERSON_NAME")
    private String name;
  
    @Column(name = "CURRENT_LOCATION")
    private Point currentLocation;   
    
    //Constructors and getters/ setters go here
}

As shown in code above, geospatial index can be specified on geolocation field(s) using @IndexCollection and @Index annotation.

Performing CRUD

Entities containing geolocation fields are saved, retrieved and deleted in the same way as normal entities. Code snippet below demonstrates this:

Save entity

EntityManagerFactory emf = Persistence.createEntityManagerFactory("mongo_pu");
EntityManager em = null;

em = emf.createEntityManager();
Person person = new Person();
person.setPersonId(1);
person.setName("Amresh");
person.setCurrentLocation(new Point(2.6, 5.4));
em.persist();
em.close();

Find Entity

em = emf.createEntityManager();
Person p = em.find(Person.class, 1);
em.close();

Update Entity

em = emf.createEntityManager();
Person p = em.find(Person.class, 1);
p.setName("Kuldeep");
em.merge(p);
em.close();

Delete Entity

em = emf.createEntityManager();
Person p = em.find(Person.class, 1);
em.remove(p);
em.close();

Running Geospatial Queries

Geospatial queries in Kundera are written in JPA-QL. Following types of queries are supported currently:

Within Queries

Within Circle:

import com.impetus.kundera.gis.geometry.Circle;

Circle circle = new Circle(3.4, 5.4, 1.0);   /x, y and radius in that order
Query q = em.createQuery("Select p from Person p where p.currentLocation IN ?1");
q.setParameter(1, circle);
List<Person> persons = q.getResultList();

Within Envelope:

import com.impetus.kundera.gis.geometry.Envelope;

Envelope envelope = new Envelope(2.0, 5.0, 2.0, 5.0);
Query q = em.createQuery("Select p from Person p where p.currentLocation IN :envelope");
q.setParameter("envelope", envelope);
List<Person> persons = q.getResultList();

Within Triangle:

import com.impetus.kundera.gis.geometry.Triangle;

Triangle triangle = new Triangle(1.0, 1.0, 5.0, 1.0, 3.0, 4.0);
Query q = em.createQuery("Select p from Person p where p.currentLocation IN :triangle");
q.setParameter("triangle", triangle);
List<Person> persons = q.getResultList();

Within polygon:

import com.impetus.kundera.gis.geometry.Polygon;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;

GeometryFactory factory = new GeometryFactory();
Coordinate[] coordinates = new Coordinate[6];

coordinates[0] = new Coordinate(1.0, 1.0);
coordinates[1] = new Coordinate(1.0, 2.0);
coordinates[2] = new Coordinate(3.0, 4, 0);
coordinates[3] = new Coordinate(4.0, 3.0);
coordinates[4] = new Coordinate(4.0, 1.0);
coordinates[5] = new Coordinate(1.0, 1.0);

CoordinateSequence points = factory.getCoordinateSequenceFactory().create(coordinates);

LinearRing shell = new LinearRing(points, factory);
LinearRing[] holes = new LinearRing[0];
Polygon polygon = new Polygon(shell, holes, factory);

Query q = em.createQuery("Select p from Person p where p.currentLocation IN :polygon");
q.setParameter("polygon", polygon);
List<Person> persons = q.getResultList();

Near Queries

import com.impetus.kundera.gis.geometry.Point;

Point point = new Point(5.0, 6.0);
Query q = em.createQuery("Select p from Person p where p.currentLocation > :point AND p.currentLocation < :maxDistance");
q.setParameter("point", point);
q.setParameter("maxDistance", 1.0);
List<Person> persons = q.getResultList();

Spherical Queries

Near Sphere:

import com.impetus.kundera.gis.geometry.Point;
import com.impetus.kundera.gis.SurfaceType;

Point point = new Point(5.0, 6.0);
point.setSurfaceType(SurfaceType.SPHERICAL);
Query q = em.createQuery("Select p from Person p where p.currentLocation > :point AND p.currentLocation < :maxDistance");
q.setParameter("point", point);
q.setParameter("maxDistance", ((2.0 * 1.0 * 3.1416 / 360.0)));  //1.0 is radius
List<Person> persons = q.getResultList();

Center Sphere:

import com.impetus.kundera.gis.geometry.Circle;
import com.impetus.kundera.gis.SurfaceType;

Circle circle = new Circle(3.4, 5.4, (2.0*2.0*3.1416/360.0));   /x, y and radius in that order
circle.setSurfaceType(SurfaceType.SPHERICAL);
Query q = em.createQuery("Select p from Person p where p.currentLocation IN ?1");
q.setParameter(1, circle);
List<Person> persons = q.getResultList();

Code example/ Test case is available at: https://github.com/impetus-opensource/Kundera/blob/trunk/kundera-mongo/src/test/java/com/impetus/client/gis/MongoGISTest.java

Home

Clone this wiki locally