Spring data Mongodb Store and query GIS Circle and Polygon - spring

I've a simple use-case to solve using MongoDB with Spring-data connectors.
Store a polygon or circle (simple Point with radius)
Check if give point is inside polygon/circle
Now, I've done like this:
Store give Polygon as a geoJSON into a document
Apply 2dSphere index
Polygon stored as a Polygon geoJSON type
Circle stored a single point and radius as separate parameter
Polygons I'm able to query using $geoIntersects
Circle I'm able to query using $geoNear with aggregation
Problem:
What is right way to store and index Circle(Point and radius) and Polygon (multiple points)?
How to do a single query to check if given point reside in Polygon / Circle?

currently Spring Data does not support this kind of query ($geoIntersects) using MongoRepository interface, but this works for me:
Let's suppose that we have the following document
#Document
public class AreaTown {
#Id
private String id;
private String name;
#GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private GeoJsonPolygon area;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GeoJsonPolygon getArea() {
return area;
}
public void setArea(GeoJsonPolygon area) {
this.area = area;
}
#Override
public String toString() {
return "AreaTown [id=" + id + ", name=" + name + ", area=" + area + "]";
}
}
Applying a custom Query, I was able to found if a point is inside of a polygon
#SpringBootApplication
public class MongoGeoJsonApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(MongoGeoJsonApplication.class, args);
}
#Autowired
private MongoTemplate mongoTemplate;
#Override
public void run(String... args) throws Exception {
Point inside = new Point(-97.406285,18.481136);
Query query = new Query().addCriteria(Criteria.where("area").intersects(new GeoJsonPoint(inside)));
List<AreaTown> list = mongoTemplate.find(query, AreaTown.class);
System.out.println(list);
}
}

Related

JHipster - Insert in the database with the GET method

I have to create an application with Jhipster but i never use it before.
When a user send a GET request to the address http://localhost:8080/api/newmesure/{mac-address}/{value}
I want to insert a new mesure in my database.
First i created 3 entity "Plantes", "Capteurs" and "Mesures" with this format :
Image here : https://i.stack.imgur.com/zJqia.png (I'm not allowed to post)
I activated the JPA Filtering to create a #Query to insert data in my database but i read that was not possible.
In /src/main/java/com/mycompany/myapp/web/rest/MesuresRessources.java :
/**
* REST controller for managing {#link com.mycompany.myapp.domain.Mesures}.
*/
#RestController
#RequestMapping("/api")
public class MesuresResource {
private final Logger log = LoggerFactory.getLogger(MesuresResource.class);
private static final String ENTITY_NAME = "mesures";
#Value("${jhipster.clientApp.name}")
private String applicationName;
private final MesuresService mesuresService;
private final MesuresQueryService mesuresQueryService;
public MesuresResource(MesuresService mesuresService, MesuresQueryService mesuresQueryService) {
this.mesuresService = mesuresService;
this.mesuresQueryService = mesuresQueryService;
}
#GetMapping("/newMesure/{mac}/{value}")
public String newMesure(#PathVariable String mac,#PathVariable int value) {
log.debug("Adresse MAC : "+mac);
log.debug("Valeur : "+value);
#Query("SELECT valeur FROM Mesures WHERE id = 1") //not working
Mesures getValeur(); //not working
return "Mesure ajoutée";
}
}
In /src/main/java/com/mycompany/myapp/domain/Mesures.java :
/**
* A Mesures.
*/
#Entity
#Table(name = "mesures")
public class Mesures implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "valeur")
private Integer valeur;
#ManyToOne(optional = false)
#NotNull
#JsonIgnoreProperties("macs")
private Capteurs mac;
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getValeur() {
return valeur;
}
public Mesures valeur(Integer valeur) {
this.valeur = valeur;
return this;
}
public void setValeur(Integer valeur) {
this.valeur = valeur;
}
public Capteurs getMac() {
return mac;
}
public Mesures mac(Capteurs capteurs) {
this.mac = capteurs;
return this;
}
public void setMac(Capteurs capteurs) {
this.mac = capteurs;
}
// jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Mesures)) {
return false;
}
return id != null && id.equals(((Mesures) o).id);
}
#Override
public int hashCode() {
return 31;
}
#Override
public String toString() {
return "Mesures{" +
"id=" + getId() +
", valeur=" + getValeur() +
"}";
}
}
Louan
Learning java with JHipster is probably not a wise idea, it uses a very rich technology stack which might lose you unless you invest enough time to learn the basics.
There are many things wrong in your code and approach:
You can't use #Query annotation inside the body of method a of your REST controller, it must be used in your #Repository interface, this code can't compile. See https://www.baeldung.com/spring-data-jpa-query for a quick introduction
JPA filtering is not related to inserting into database
In HTTP/REST, GET method is supposed to be idempotent. For making changes in your database you should use POST or PUT methods. See What is idempotency in HTTP methods?
Your entity naming convention is not consistent: use singular for entity classes because each entity object represents one single instance of Mesure. Here you have Plantes (plural), Capteur (singular) and Mesures (plural). For table names, JHipster uses singular but plural is quite common too because a table holds many rows. Of course, this is just a convention and you or your team may decide to apply another (like a prefix for table names) but the key point is to be consistent.

Relationship Exists in neo4j but not in Spring #NodeEntity

I have a class in my domain called Activity that looks like the following
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#NodeEntity
public class Activity {
#GraphId
private Long id;
private String title;
private String description;
#Relationship(type = "RELATED_TO", direction = Relationship.UNDIRECTED)
private List<Activity> relatedActivities = new ArrayList<>();
public Activity() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public Collection<Activity> getRelatedActivities() {
System.out.println("getting relatedActivities");
System.out.println(relatedActivities);
return relatedActivities;
}
public void addRelatedActivity(Activity activity) {
this.relatedActivities.add(activity);
}
}
I create relationships using the following repository class:
#RepositoryRestResource(collectionResourceRel = "relationships", path = "relationships")
public interface RelationshipRepository extends GraphRepository<Relationship> {
#Query("MATCH (a1:Activity), (a2:Activity) " +
"WHERE a1.title = {0} AND a2.title = {1}" +
"CREATE (a1)-[:RELATED_TO]->(a2)")
void addRelationship(String a1Title, String a2Title);
}
I have verified that this code works using the neo4j browser, which lets me see existing nodes and relationships between them. However, when I access getRelatedActivities() on an Activity object, it's always an empty array, even if that Activity has other Activity nodes related to it, clearly visible in neo4j.
How can I get the relatedActivites on an Activity to automatically populate based on its relationships correctly?
The problem in your code is that you define the "target" as an Activity here
#Relationship(type = "RELATED_TO", direction = Relationship.UNDIRECTED)
private List<Activity> relatedActivities = new ArrayList<>();
but you also have a RelationshipEntity class in your code base: Relationship with the same type RELATED_TO.
When OGM gets the result it tries to match every field but since it converts the relationship type RELATED_TO to the RelationshipEntity and not an Activity object, it does not fill the list in the Activity class.

problems with FindByLocationWithin

I'm trying to query a mongo repository to return data that is within a specified geo circle. I'm using the following code:
Page<Img> findByLocationWithin(Circle circle, Pageable pageable);
and then in my controller I'm using:
Distance distance = new Distance(7.5, Metrics.MILES);
Circle circle = new Circle(location, distance);
Page<Img> results = imgRepository.findByLocationWithin(circle, pageable);
However it definitely doesn't use a radius of 7.5 miles as if I create the circle a few hundred metres away from where the data is located, it returns nothing. I've checked the logs in mongo and it says that the following code is being performed:
"location" : {
"$within" : {
"$center" : [
[
30.198,
-1.695
],
0.0018924144710663706
]
}
}
This means it's not using $geoWithin or $centerSphere. How can I fix these problems?
I had the same problem with spring and Couchbase... but the query is not the problem... Because Spring convert the distance in the geometric values.
In my case I also returned null, but my problem was solved, in the model class, the attribute that specifies the coordinate [x, y] must be of type Library Point org.springframework.data.geo.Point;
package com.webServices.rutas.model;
import org.springframework.data.couchbase.core.mapping.id.GeneratedValue;
import org.springframework.data.couchbase.core.mapping.id.GenerationStrategy;
import org.springframework.data.geo.Point;
import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;
public class Parada {
#Id #GeneratedValue(strategy = GenerationStrategy.UNIQUE)
private String id;
#Field
private String type;
#Field
private String nombre;
#Field
private String urlFoto;
#Field
private Point coordenada;
public Parada(String nombre, String urlFoto, Point coordenada) {
super();
this.type = "parada";
this.nombre = nombre;
this.urlFoto = urlFoto;
this.coordenada = coordenada;
}
public Parada() {
super();
this.type = "parada";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getUrlFoto() {
return urlFoto;
}
public void setUrlFoto(String urlFoto) {
this.urlFoto = urlFoto;
}
public Point getCoordenada() {
return coordenada;
}
public void setCoordenada(Point coordenada) {
this.coordenada = coordenada;
}
#Override
public String toString() {
return "Parada [id=" + id + ", type=" + type + ", nombre=" + nombre + ", urlFoto=" + urlFoto + ", coordenada="
+ coordenada + "]";
}
}
In the service:
public Iterable<Parada> getParadasCercanasRadio(Punto punto){
Point cuadro = new Point(-2.2,-80.9);
Circle circle = new Circle(cuadro,new Distance(300000, Metrics.KILOMETERS));
return paradaRepository.findByCoordenadaWithin(circle);
}
In the Repository:
#Dimensional(designDocument = "paradas", spatialViewName = "paradas")
Iterable<Parada> findByCoordenadaWithin(Circle p);
P.D. Sorry for my English.

Spring dynamic Qualifier

I have an interface Shape
public interface Shape {
String draw();
}
And two implementations for the above Shape interface
#Component("triangle")
public class Triangle implements Shape {
public String draw() {
return "drawing Triangle";
}
}
and
#Component("circle")
public class Circle implements Shape {
public String draw() {
return "Drawing Circle";
}
}
In my client code if I have to decide which Shape class to use at run time based on Qualifier
#Autowired
private Shape shape;
#GET
#Produces(MediaType.TEXT_HTML)
#Path("/shape")
public String getShape(#PathParam("type")
String type) {
String a = shape.draw();
return a;
}
How to do it?
I want to pass "type" which I get as a pathparam to decide which Shape object to be injected at run time. Please help me in resolving this issue.
I could figure out one way of getting around this by injecting ApplicationContext in the client code.
#Autowired
private ApplicationContext appContext;
And looking up for the bean with the help of getBean method:
#GET
#Produces(MediaType.TEXT_HTML)
#Path("/shape/{type}")
public String getShape(#PathParam("type")
String type) {
Shape shape = appContext.getBean(type, Shape.class);
return shape.draw();
}
I hope this would help someone :)

How to disply timdstamp column with specific format in Spring Data JPA

I am using Spring Data JPA and I have a table like below:
public class Apk {
#Temporal(TemporalType.TIMESTAMP)
#Column
private java.util.Date creationTime;
}
My DBMS is MySQL5.x and the above column is defined datetime type in it. I just call findAl() method in a repository class extends PaginAndSortingRepository.
public interface ApksRepository extends PaginAndSortingRepository<Apk, Long>{
}
public class ApksServiceImpl implements ApksService {
public PagingRes<Apk> findAll(PageInfo pageInfo){
PaginRes<Apk> result = new PagingRes<Apk>();
Page page = apksRepos.findAll(pageInfo.toPageRequest());
result.fromPage(page);
return result;
}
}
public class PageInfo {
private int page;
private int rp;
private String sortname;
private String sortorder;
private String query;
private String qtype;
//getters and setters
public PageRequest toPageRequest() {
Sort.Direction direction = Sort.Direction.ASC;
if (sortorder!=null && "DESC".equals(sortorder))
direction = Sort.Direction.DESC;
return new PageRequest(page-1, rp, direction, sortname);
}
}
public class PagingRes<T> {
private long total;
private int page;
private int rowPerPage;
private List<T> rows;
//getters and setters
public PagingRes<T> fromPage(Page page) {
this.page = page.getNumber();
this.rowPerPage = page.getSize();
this.total = page.getTotalElements();
this.rows = page.getCotent();
return this;
}
}
And I am trying to display data in the table including the column but when I did it, the column is shown as long type. I wan to display the column in the fomat 'dd-MM-yyyy hh:mm:ss'. How can I do this?
Thanks in Advance.
I knew that Jackson mapper for JSON response were giving weird result. So after searching, I used JsonSerializer to fix it like below:
Entity class
#JsonSerialize(using=AuDateSerializer.class)
private java.util.Date eventTime;
Custom Serializer
public class AuDateSerializerextends JsonSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
jgen.writeString(formatter.format(value));
}
}
Now it works fine. Thanks.

Resources