package org.activityinfo.server.database.hibernate.dao; import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Provider; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import org.activityinfo.model.type.geo.AiLatLng; import org.activityinfo.server.database.hibernate.entity.AdminEntity; import org.activityinfo.server.util.mapping.JtsUtil; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.spatial.criterion.SpatialRestrictions; import java.util.List; /** * Translates a lat/lng coordinate into a list of administrative entities */ public class Geocoder { private final Provider<Session> sessionProvider; private final GeometryFactory gf = new GeometryFactory(); @Inject public Geocoder(Provider<Session> sessionProvider) { this.sessionProvider = sessionProvider; } /** * Geocode a single point to a list of admin entities * * @param latitude * @param longitude * @return */ public List<AdminEntity> geocode(double latitude, double longitude) { Point point = gf.createPoint(new Coordinate(longitude, latitude)); Session session = sessionProvider.get(); Criteria criteria = session.createCriteria(AdminEntity.class); criteria.add(SpatialRestrictions.contains("geometry", point)); // mysql seems to check only the MBRs. We need to verify // that the point is actually contained by the geometry List<AdminEntity> containedByMbr = criteria.list(); List<AdminEntity> contains = Lists.newArrayList(); for (AdminEntity entity : containedByMbr) { if (JtsUtil.contains(entity.getGeometry(), point)) { contains.add(entity); } } return contains; } public List<List<AdminEntity>> gecodeBatch(List<AiLatLng> points) { BatchGeocoder batchGeocoder = new BatchGeocoder(sessionProvider.get()); for (AiLatLng latLng : points) { batchGeocoder.addPoint(latLng.getLng(), latLng.getLat()); } return batchGeocoder.geocode(); } }