/*******************************************************************************
* 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.reversegeocoding;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import com.gisgraphy.addressparser.Address;
import com.gisgraphy.addressparser.AddressResultsDto;
import com.gisgraphy.addressparser.AddressResultsDtoSerializer;
import com.gisgraphy.domain.geoloc.entity.City;
import com.gisgraphy.domain.geoloc.entity.OpenStreetMap;
import com.gisgraphy.domain.repository.ICityDao;
import com.gisgraphy.domain.repository.IOpenStreetMapDao;
import com.gisgraphy.domain.valueobject.Constants;
import com.gisgraphy.importer.LabelGenerator;
import com.gisgraphy.serializer.common.UniversalSerializerConstant;
import com.gisgraphy.service.IStatsUsageService;
import com.gisgraphy.service.ServiceException;
import com.gisgraphy.stats.StatsUsageType;
import com.vividsolutions.jts.geom.Point;
/**
* Default (threadsafe) implementation of {@link IReverseGeocodingService}.
*
* @author <a href="mailto:david.masclet@gisgraphy.com">David Masclet</a>
*
*/
@Service
public class ReverseGeocodingService implements IReverseGeocodingService {
private static final ArrayList<Address> NO_ADDRESS_LIST = new ArrayList<Address>();
@Autowired
protected IOpenStreetMapDao openStreetMapDao;
@Resource
protected IStatsUsageService statsUsageService;
protected LabelGenerator labelGenerator = LabelGenerator.getInstance();
@Autowired
protected ICityDao cityDao;
AddressResultsDtoSerializer addressResultsDtoSerializer = new AddressResultsDtoSerializer();
/**
* The logger
*/
protected static final Logger logger = LoggerFactory
.getLogger(ReverseGeocodingService.class);
public static int DEFAULT_CITY_RADIUS = 30000;
public static int DEFAULT_STREET_RADIUS = 1000;
public AddressResultsDto executeQuery(ReverseGeocodingQuery query)
throws ServiceException {
Assert.notNull(query, "Can not execute a null query");
Point point = query.getPoint();
Assert.notNull(point, "Can not execute a query without a valid point");
long start = System.currentTimeMillis();
statsUsageService.increaseUsage(StatsUsageType.REVERSEGEOCODING);
OpenStreetMap openStreetMap = openStreetMapDao.getNearestRoadFrom(point,DEFAULT_STREET_RADIUS);
//AddressResultsDto addressResultsDto = null;
if (openStreetMap==null){
logger.debug("no road found, try to search deeper");
openStreetMap =openStreetMapDao.getNearestFrom(point,DEFAULT_STREET_RADIUS);
}
if (openStreetMap!= null){
//stupid patch
/* logger.error("patch admname");
City city = cityDao.getByShape(point, null, false);
if (city == null){
logger.info("No city by shape found, try by vicinity");
city = cityDao.getNearest(point,null, false, DEFAULT_RADIUS);
if (city == null){
logger.error("No city by vicinity found");
} else {
logger.error("find a city by vicinity "+city.getName()+" / "+city.getId()+"/"+city.getAdm1Name());
if (city.getAdm1Name()!= null){
openStreetMap.setIsInAdm(city.getAdm1Name());
}
}
} else {
logger.info("find a city by shape "+city.getName()+" / "+city.getId()+"/"+city.getAdm1Name());
if (city.getAdm1Name()!= null){
openStreetMap.setIsInAdm(city.getAdm1Name());
}
}*/
//end of stupid patch
logger.debug("found a street "+openStreetMap);
if (openStreetMap.getHouseNumbers()!=null && openStreetMap.getHouseNumbers().size() >=1 ){
logger.debug("the street has "+openStreetMap.getHouseNumbers().size()+" housenumbers");
HouseNumberDistance houseNumberDistance = labelGenerator.getNearestHouse(openStreetMap.getHouseNumbers(), point);
if (houseNumberDistance!=null){
Address address = labelGenerator.buildAddressFromHouseNumberDistance(houseNumberDistance);
if (address!=null){
logger.info("found an address at a house number level");
List<Address> addresses = new ArrayList<Address>();
addresses.add(address);
long end = System.currentTimeMillis();
long qTime = end - start;
logger.info(query + " took " + (qTime) + " ms and returns a result");
//addressResultsDto = new AddressResultsDto(addresses, qTime);
return new AddressResultsDto(addresses, qTime);
} else {
logger.info("found an address at a street level");
List<Address> addresses = new ArrayList<Address>();
addresses.add(address);
long end = System.currentTimeMillis();
long qTime = end - start;
logger.info(query + " took " + (qTime) + " ms and returns a result");
return new AddressResultsDto(addresses, qTime);
}
}
} else {
logger.debug("the street has no housenumbers");
Address address = labelGenerator.buildAddressFromOpenstreetMapAndPoint(openStreetMap,point);
if (address!=null){
List<Address> addresses = new ArrayList<Address>();
addresses.add(address);
long end = System.currentTimeMillis();
long qTime = end - start;
logger.info(query + " took " + (qTime) + " ms and returns a result");
return new AddressResultsDto(addresses, qTime);
}
}
} else {
logger.info("No street found, try to find city by shape");
City city = cityDao.getByShape(point, null, false);
Address address = null;
if (city == null){
logger.info("No city by shape found, try by vicinity");
city = cityDao.getNearest(point,null, false, DEFAULT_CITY_RADIUS);
if (city == null){
logger.info("No city by vicinity found");
} else {
logger.info("find a city by vicinity "+city.getName()+" / "+city.getId());
address = labelGenerator.buildAddressFromCityAndPoint(city,point);
}
} else {
logger.info("find a city by shape "+city.getName()+" / "+city.getId());
address = labelGenerator.buildAddressFromCityAndPoint(city,point);
}
List<Address> addresses = new ArrayList<Address>();
if (address != null){
addresses.add(address);
}
long end = System.currentTimeMillis();
long qTime = end - start;
if (address==null){
logger.info(query + " took " + (qTime) + " ms and returns no result");
} else {
logger.info(query + " took " + (qTime) + " ms and returns a city : "+address.getCity());
}
return new AddressResultsDto(addresses, qTime);
}
long end = System.currentTimeMillis();
long qTime = end - start;
logger.info(query + " took " + (qTime) + " ms and doesn't returns no result");
return new AddressResultsDto(NO_ADDRESS_LIST, qTime);
}
/*
* (non-Javadoc)
*
* @see com.gisgraphy.domain.geoloc.service.IQueryProcessor#executeAndSerialize(com.gisgraphy.domain.geoloc.service.AbstractGisQuery,
* java.io.OutputStream)
*/
public void executeAndSerialize(ReverseGeocodingQuery query, OutputStream outputStream)
throws ServiceException {
Assert.notNull(query, "Can not execute a null query");
Assert.notNull(outputStream,
"Can not serialize into a null outputStream");
AddressResultsDto AddressResultDto = executeQuery(query);
Map<String, Object> extraParameter = new HashMap<String, Object>();
extraParameter.put(UniversalSerializerConstant.CALLBACK_METHOD_NAME, query.getCallback());
addressResultsDtoSerializer.serialize(outputStream, query.getOutputFormat(), AddressResultDto, false,extraParameter);
}
/*
* (non-Javadoc)
*
* @see com.gisgraphy.domain.geoloc.service.IQueryProcessor#executeQueryToString(com.gisgraphy.domain.geoloc.service.AbstractGisQuery)
*/
public String executeQueryToString(ReverseGeocodingQuery query)
throws ServiceException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
executeAndSerialize(query, outputStream);
try {
return outputStream.toString(Constants.CHARSET);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("unknow encoding "+Constants.CHARSET);
}
}
}