/*******************************************************************************
* Gisgraphy Project
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*
* Copyright 2008 Gisgraphy project
* David Masclet <davidmasclet@gisgraphy.com>
*
*
*******************************************************************************/
package com.gisgraphy.domain.repository;
import static com.gisgraphy.hibernate.projection.SpatialProjection.DISTANCE_SPHERE_FUNCTION;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.PersistenceException;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.NotImplementedException;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
import com.gisgraphy.GisgraphyException;
import com.gisgraphy.domain.geoloc.entity.AlternateOsmName;
import com.gisgraphy.domain.geoloc.entity.GisFeature;
import com.gisgraphy.domain.geoloc.entity.OpenStreetMap;
import com.gisgraphy.domain.geoloc.entity.Street;
import com.gisgraphy.domain.geoloc.entity.event.EventManager;
import com.gisgraphy.domain.geoloc.entity.event.GisFeatureDeletedEvent;
import com.gisgraphy.domain.geoloc.entity.event.GisFeatureStoredEvent;
import com.gisgraphy.domain.geoloc.entity.event.PlaceTypeDeleteAllEvent;
import com.gisgraphy.domain.valueobject.GisgraphyConfig;
import com.gisgraphy.domain.valueobject.SRID;
import com.gisgraphy.domain.valueobject.StreetDistance;
import com.gisgraphy.helper.GeolocHelper;
import com.gisgraphy.helper.IntrospectionHelper;
import com.gisgraphy.helper.StringHelper;
import com.gisgraphy.hibernate.criterion.DistanceRestriction;
import com.gisgraphy.hibernate.criterion.IntersectsRestriction;
import com.gisgraphy.hibernate.criterion.NativeSQLOrder;
import com.gisgraphy.hibernate.criterion.ProjectionOrder;
import com.gisgraphy.hibernate.criterion.ResultTransformerUtil;
import com.gisgraphy.hibernate.projection.ProjectionBean;
import com.gisgraphy.hibernate.projection.SpatialProjection;
import com.gisgraphy.street.IStreetFactory;
import com.gisgraphy.street.StreetFactory;
import com.gisgraphy.street.StreetSearchMode;
import com.gisgraphy.street.StreetType;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* A data access object for {@link OpenStreetMap} Object
*
* @author <a href="mailto:david.masclet@gisgraphy.com">David Masclet</a>
*/
@Repository
@SuppressWarnings({ "unchecked", "deprecation", "rawtypes" })
public class OpenStreetMapDao extends GenericDao<OpenStreetMap, Long> implements IOpenStreetMapDao
{
private IStreetFactory streetFactory = new StreetFactory();
private EventManager eventManager;
/**
* The logger
*/
protected static final Logger logger = LoggerFactory
.getLogger(OpenStreetMapDao.class);
protected static final int DEFAULT_DISTANCE = 7000;
/**
* Default constructor
*/
public OpenStreetMapDao() {
super(OpenStreetMap.class);
}
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#getNearestAndDistanceFrom(com.vividsolutions.jts.geom.Point, double, int, int, java.lang.String, java.lang.String)
*/
public List<StreetDistance> getNearestAndDistanceFrom(
final Point point, final double distance,
final int firstResult, final int maxResults,
final StreetType streetType, final Boolean oneWay ,final String name, final StreetSearchMode streetSearchMode,final boolean includeDistanceField) {
if (streetSearchMode==StreetSearchMode.FULLTEXT && !GisgraphyConfig.STREET_SEARCH_FULLTEXT_MODE){
throw new GisgraphyException("The fulltext mode has been removed in gisgraphy v 3.0 and has been replaced by fulltext webservice with placetype=street. please Consult user guide.");
}
if (name != null && streetSearchMode==null){
throw new IllegalArgumentException("streetSearchmode can not be null if name is provided");
}
if (point == null && streetSearchMode==StreetSearchMode.CONTAINS){
throw new IllegalArgumentException("you must specify lat/lng when streetsearchmode = "+StreetSearchMode.CONTAINS);
}
return (List<StreetDistance>) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
Criteria criteria = session
.createCriteria(OpenStreetMap.class);
List<String> fieldList = IntrospectionHelper
.getFieldsAsList(OpenStreetMap.class);
ProjectionList projections = ProjectionBean.fieldList(
fieldList,false);
if (includeDistanceField && point!=null){
projections.add(
// SpatialProjection.distance_sphere(point, GisFeature.LOCATION_COLUMN_NAME).as(
// "distance"));
SpatialProjection.distance_pointToLine(point, OpenStreetMap.SHAPE_COLUMN_NAME).as(
"distance"));
}
criteria.setProjection(projections);
if (includeDistanceField && point !=null){
criteria.addOrder(new ProjectionOrder("distance"));
}
if (maxResults > 0) {
criteria = criteria.setMaxResults(maxResults);
}
if (firstResult >= 1) {
criteria = criteria.setFirstResult(firstResult - 1);
}
if (point!=null){
Polygon polygonBox = GeolocHelper.createPolygonBox(point.getX(), point.getY(), distance);
criteria = criteria.add(new IntersectsRestriction(OpenStreetMap.SHAPE_COLUMN_NAME, polygonBox));
}
if (name != null) {
if (streetSearchMode==StreetSearchMode.CONTAINS){
criteria = criteria.add(Restrictions.isNotNull("name"));//optimisation!
criteria = criteria.add(Restrictions.ilike(OpenStreetMap.FULLTEXTSEARCH_PROPERTY_NAME, "%"+StringHelper.normalize(name)+"%"));
//criteria = criteria.add(new PartialWordSearchRestriction(OpenStreetMap.PARTIALSEARCH_VECTOR_COLUMN_NAME, name));
} /*else if (streetSearchMode == StreetSearchMode.FULLTEXT){
criteria = criteria.add(new FulltextRestriction(OpenStreetMap.FULLTEXTSEARCH_VECTOR_PROPERTY_NAME, name));
} */else {
throw new NotImplementedException(streetSearchMode+" is not implemented for street search");
}
}
if (streetType != null) {
criteria = criteria.add(Restrictions.eq("streetType",streetType));
}
if (oneWay != null) {
criteria = criteria.add(Restrictions.eq("oneWay",oneWay));
}
criteria.setCacheable(true);
// List<Object[]> queryResults =testCriteria.list();
List<?> queryResults = criteria.list();
if (queryResults != null && queryResults.size()!=0){
String[] propertiesNameArray ;
if (includeDistanceField && point!=null){
propertiesNameArray = (String[]) ArrayUtils
.add(
IntrospectionHelper
.getFieldsAsArray(OpenStreetMap.class),
"distance");
} else {
propertiesNameArray = IntrospectionHelper
.getFieldsAsArray(OpenStreetMap.class);
}
List<StreetDistance> results = ResultTransformerUtil
.transformToStreetDistance(
propertiesNameArray,
queryResults);
return results;
} else {
return new ArrayList<StreetDistance>();
}
}
});
}
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#getByGid(java.lang.Long)
*/
public OpenStreetMap getByGid(final Long gid) {
Assert.notNull(gid);
return (OpenStreetMap) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
String queryString = "from "
+ OpenStreetMap.class.getSimpleName()
+ " as c where c.gid= ?";
Query qry = session.createQuery(queryString);
qry.setCacheable(true);
qry.setParameter(0, gid);
OpenStreetMap result = (OpenStreetMap) qry.uniqueResult();
return result;
}
});
}
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#buildIndexForStreetNameSearch()
*/
/* public Integer updateTS_vectorColumnForStreetNameSearch() {
return (Integer) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
session.flush();
logger.info("will update "+OpenStreetMap.FULLTEXTSEARCH_VECTOR_PROPERTY_NAME.toLowerCase()+" field");
String updateFulltextField = "UPDATE openStreetMap SET "+OpenStreetMap.FULLTEXTSEARCH_VECTOR_PROPERTY_NAME.toLowerCase()+" = to_tsvector('simple',coalesce("+OpenStreetMap.FULLTEXTSEARCH_COLUMN_NAME+",'')) where name is not null";
Query qryUpdateFulltextField = session.createSQLQuery(updateFulltextField);
int numberOfLineUpdatedForFulltext = qryUpdateFulltextField.executeUpdate();
int numberOfLineUpdatedForPartial = 0;
return Integer.valueOf(numberOfLineUpdatedForFulltext + numberOfLineUpdatedForPartial);
}
});
}*/
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#buildIndexForStreetNameSearch()
*/
/* public Integer updateTS_vectorColumnForStreetNameSearchPaginate(final long from,final long to ) {
return (Integer) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
session.flush();
logger.info("will update "+OpenStreetMap.FULLTEXTSEARCH_VECTOR_PROPERTY_NAME.toLowerCase()+" field");
String updateFulltextField = "UPDATE openStreetMap SET "+OpenStreetMap.FULLTEXTSEARCH_VECTOR_PROPERTY_NAME.toLowerCase()+" = to_tsvector('simple',coalesce("+OpenStreetMap.FULLTEXTSEARCH_COLUMN_NAME+",'')) where gid >= "+from+" and gid <= "+to+" and name is not null";
Query qryUpdateFulltextField = session.createSQLQuery(updateFulltextField);
int numberOfLineUpdatedForFulltext = qryUpdateFulltextField.executeUpdate();
int numberOfLineUpdatedForPartial = 0;
return Integer.valueOf(numberOfLineUpdatedForFulltext + numberOfLineUpdatedForPartial);
}
});
}*/
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#createGISTIndex()
*/
public void createSpatialIndexes() {
this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
session.flush();
String locationIndexName = OpenStreetMap.LOCATION_COLUMN_NAME.toLowerCase()+"indexopenstreetmap";
logger.info("checking if "+locationIndexName+" exists");
String checkingLocationIndex= "SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = '"+locationIndexName+"'";
Query checkingLocationIndexQuery = session.createSQLQuery(checkingLocationIndex);
Object locationIndexExists = checkingLocationIndexQuery.uniqueResult();
if (locationIndexExists != null){
logger.info("will create GIST index for the "+OpenStreetMap.LOCATION_COLUMN_NAME+" column");
String createIndexForLocation = "CREATE INDEX "+locationIndexName+" ON openstreetmap USING GIST ("+OpenStreetMap.LOCATION_COLUMN_NAME.toLowerCase()+")";
Query qryUpdateLocationIndex = session.createSQLQuery(createIndexForLocation);
qryUpdateLocationIndex.executeUpdate();
} else {
logger.info("won't create GIST index for the "+OpenStreetMap.LOCATION_COLUMN_NAME+" column because it already exists");
}
String shapeIndexName=OpenStreetMap.SHAPE_COLUMN_NAME.toLowerCase()+"indexopenstreetmap";
logger.info("checking if "+shapeIndexName+" exists");
String checkingShapeIndex= "SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = '"+shapeIndexName+"'";
Query checkingShapeIndexQuery = session.createSQLQuery(checkingShapeIndex);
Object shapeIndexExists = checkingShapeIndexQuery.uniqueResult();
if (shapeIndexExists!=null){
logger.info("will create GIST index for the "+OpenStreetMap.SHAPE_COLUMN_NAME+" column");
String createIndexForShape = "CREATE INDEX "+OpenStreetMap.SHAPE_COLUMN_NAME.toLowerCase()+"indexopenstreetmap ON openstreetmap USING GIST ("+OpenStreetMap.SHAPE_COLUMN_NAME.toLowerCase()+")";
Query qryUpdateShapeIndex = session.createSQLQuery(createIndexForShape);
qryUpdateShapeIndex.executeUpdate();
} else {
logger.info("won't create GIST index for the "+OpenStreetMap.SHAPE_COLUMN_NAME+" column because it already exists");
}
return null;
}
});
}
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#createGISTIndex()
*/
/* public void createFulltextIndexes() {
this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
session.flush();
logger.info("will create Fulltext index");
String createFulltextIndex = "CREATE INDEX "+OpenStreetMap.FULLTEXTSEARCH_VECTOR_PROPERTY_NAME.toLowerCase()+"indexopenstreetmap ON openstreetmap USING gin("+OpenStreetMap.FULLTEXTSEARCH_VECTOR_PROPERTY_NAME.toLowerCase()+")";
Query fulltextIndexQuery = session.createSQLQuery(createFulltextIndex);
fulltextIndexQuery.executeUpdate();
return null;
}
});
}*/
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#countEstimate()
*/
public long countEstimate(){
return (Long) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
String queryString = "select max(gid)-min(gid)+1 from "
+ persistentClass.getSimpleName();
Query qry = session.createQuery(queryString);
qry.setCacheable(true);
Long count = (Long) qry.uniqueResult();
return count==null?0:count;
}
});
}
@Override
public OpenStreetMap save(OpenStreetMap openStreetMap) {
OpenStreetMap savedEntity = super.save(openStreetMap);
Street street = streetFactory.create(savedEntity);
GisFeatureStoredEvent CreatedEvent = new GisFeatureStoredEvent(
street);
eventManager.handleEvent(CreatedEvent);
return savedEntity;
}
public String getShapeAsWKTByGId(final Long gid) {
if (gid ==null){
return null;
}
return (String) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
String queryString = "select st_astext("+GisFeature.SHAPE_COLUMN_NAME+") from " + persistentClass.getSimpleName()
+ " as o where o.gid=?";
Query qry = session.createQuery(queryString);
qry.setParameter(0, gid);
// qry.setCacheable(true);
Object shape = qry.uniqueResult();
if (shape !=null){
return shape.toString();
} else {
return null;
}
}
});
}
/*
* (non-Javadoc)
*
* @see com.gisgraphy.domain.repository.GenericDao#remove(java.lang.Object)
*/
@Override
public void remove(OpenStreetMap openStreetMap) {
super.remove(openStreetMap);
Street street = streetFactory.create(openStreetMap);
GisFeatureDeletedEvent gisFeatureDeletedEvent = new GisFeatureDeletedEvent(
street);
eventManager.handleEvent(gisFeatureDeletedEvent);
}
/*
* (non-Javadoc)
*
* @see com.gisgraphy.domain.repository.GenericDao#deleteAll()
*/
@Override
public int deleteAll() {
int numberOfOpenStreetMapDeleted = super.deleteAll();
PlaceTypeDeleteAllEvent placeTypeDeleteAllEvent = new PlaceTypeDeleteAllEvent(
Street.class);
eventManager.handleEvent(placeTypeDeleteAllEvent);
return numberOfOpenStreetMapDeleted;
}
public OpenStreetMap getByOpenStreetMapId(final Long openstreetmapId) {
Assert.notNull(openstreetmapId);
return (OpenStreetMap) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
String queryString = "from "
+ OpenStreetMap.class.getSimpleName()
+ " as c where c.openstreetmapId= ?";
Query qry = session.createQuery(queryString);
qry.setMaxResults(1);
//we need to limit to 1 because a street can be in two countries
qry.setCacheable(true);
qry.setParameter(0, openstreetmapId);
OpenStreetMap result = (OpenStreetMap) qry.uniqueResult();
return result;
}
});
}
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#getMaxOpenstreetMapId()
*/
public long getMaxOpenstreetMapId() {
return (Long) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
String queryString = "select max(openstreetmapId) from "
+ OpenStreetMap.class.getSimpleName();
Query qry = session.createQuery(queryString);
qry.setCacheable(true);
Long count = (Long) qry.uniqueResult();
return count==null?0:count;
}
});
}
public OpenStreetMap getNearestByosmIds(final Point point,final List<Long> ids) {
if (ids==null || ids.size()==0){
return null;
}
return (OpenStreetMap) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
Criteria criteria = session
.createCriteria(OpenStreetMap.class);
criteria.add(new DistanceRestriction(point,DEFAULT_DISTANCE,true));
criteria.add(Restrictions.in("openstreetmapId", ids));
String pointAsString = "ST_GeometryFromText('POINT("+point.getX()+" "+point.getY()+")',"+SRID.WGS84_SRID.getSRID()+")";
String distanceCondition = new StringBuffer()
.append(DISTANCE_SPHERE_FUNCTION)
.append("(")
.append(pointAsString)
.append(",")
.append(SpatialProjection.ST_CLOSEST_POINT)
.append("(")
.append("this_.").append(OpenStreetMap.SHAPE_COLUMN_NAME)
.append(",")
.append(pointAsString)
.append(")")
.append(")")
.toString();
criteria.addOrder(new NativeSQLOrder(distanceCondition));
criteria = criteria.setMaxResults(1);
//criteria.setCacheable(true);
// List<Object[]> queryResults =testCriteria.list();
OpenStreetMap openStreetMap = (OpenStreetMap)criteria.uniqueResult();
return openStreetMap;
}
});
}
/* (non-Javadoc)
* @see com.gisgraphy.domain.repository.IOpenStreetMapDao#getMaxOpenstreetMapId()
*/
public long getMaxGid() {
return (Long) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
String queryString = "select max(gid) from "
+ OpenStreetMap.class.getSimpleName();
Query qry = session.createQuery(queryString);
qry.setCacheable(true);
Long count = (Long) qry.uniqueResult();
return count==null?0:count;
}
});
}
@Autowired
public void setEventManager(EventManager eventManager) {
this.eventManager = eventManager;
}
public void setStreetFactory(IStreetFactory streetFactory) {
this.streetFactory = streetFactory;
}
//*************************don't search for name****************************************************
public OpenStreetMap getNearestFrom(final Point point) {
return getNearestFrom(point,DEFAULT_DISTANCE) ;
}
public OpenStreetMap getNearestRoadFrom(final Point point,final double distance) {
return getNearestFrom(point,true,true, distance);
}
public OpenStreetMap getNearestFrom(final Point point,final double distance) {
return getNearestFrom(point,false,false, distance);
}
public OpenStreetMap getNearestFrom(
final Point point,final boolean onlyroad,final boolean filterEmptyName, final double distance) {
if (point==null){
return null;
}
List<OpenStreetMap> results = getNearestsFrom(point, onlyroad, filterEmptyName, distance);
if (results!=null && results.size()>0){
return results.get(0);
} else {
return null;
}
}
public List<OpenStreetMap> getNearestsFrom(
final Point point,final boolean onlyroad,final boolean filterEmptyName, final double distance) {
if (point==null){
return null;
}
return (List<OpenStreetMap>) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws PersistenceException {
Criteria criteria = session
.createCriteria(OpenStreetMap.class);
if (point!=null){
//An intersect restriction will probably have better performances and use the index than a distance restriction
Polygon polygonBox = GeolocHelper.createPolygonBox(point.getX(), point.getY(), distance);
criteria = criteria.add(new IntersectsRestriction(OpenStreetMap.SHAPE_COLUMN_NAME, polygonBox));
}
if (onlyroad) {
criteria = criteria.add(Restrictions.ne("streetType",StreetType.FOOTWAY));
}
if (filterEmptyName){
criteria = criteria.add(Restrictions.isNotNull("name"));
}
String pointAsString = "ST_GeometryFromText('POINT("+point.getX()+" "+point.getY()+")',"+SRID.WGS84_SRID.getSRID()+")";
String distanceCondition = new StringBuffer()
.append(DISTANCE_SPHERE_FUNCTION)
.append("(")
.append(pointAsString)
.append(",")
.append(SpatialProjection.ST_CLOSEST_POINT)
.append("(")
.append("this_.").append(OpenStreetMap.SHAPE_COLUMN_NAME)
.append(",")
.append(pointAsString)
.append(")")
.append(")")
.toString();
criteria.addOrder(new NativeSQLOrder(distanceCondition));
criteria.setCacheable(true);
// List<Object[]> queryResults =testCriteria.list();
List<OpenStreetMap> openStreetMap = (List<OpenStreetMap>)criteria.list();
return openStreetMap;
}
});
}
//***************************search for name***************************************************
public OpenStreetMap getNearestFromByName(
final Point point,final double distance, final String name) {
if (name ==null){
return getNearestFrom(point, distance);
}
List<OpenStreetMap> results = getNearestsFrom(point, false, true, distance);
if (results!=null && results.size()>0){
for (OpenStreetMap openstreetmap:results){
if (StringHelper.isSameStreetName(name, openstreetmap)){
return openstreetmap;
}
}
}
return null;
}
}