/******************************************************************************* * 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.importer; import java.io.File; import java.util.ArrayList; import java.util.List; import org.hibernate.FlushMode; import org.hibernate.exception.ConstraintViolationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import com.gisgraphy.domain.geoloc.entity.City; import com.gisgraphy.domain.geoloc.entity.GisFeature; import com.gisgraphy.domain.repository.IGisFeatureDao; import com.gisgraphy.domain.valueobject.GISSource; import com.gisgraphy.domain.valueobject.NameValueDTO; import com.gisgraphy.helper.GeolocHelper; import com.vividsolutions.jts.geom.Geometry; /** * Import the quattroshapes localities from an (pre-processed) Quattroshapes data file. * The goal of this importer is to cross information between geonames and Quattroshapes * and add shape to cities * * * @author <a href="mailto:david.masclet@gisgraphy.com">David Masclet</a> */ public class QuattroshapesSimpleImporter extends AbstractSimpleImporterProcessor { protected static final Logger logger = LoggerFactory.getLogger(QuattroshapesSimpleImporter.class); protected IGisFeatureDao gisFeatureDao; /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#flushAndClear() */ @Override protected void flushAndClear() { gisFeatureDao.flushAndClear(); } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#getFiles() */ @Override protected File[] getFiles() { return ImporterHelper.listCountryFilesToImport(importerConfig.getQuattroshapesDir()); } @Override Integer getImportKey() { return 0; } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#getNumberOfColumns() */ @Override protected int getNumberOfColumns() { return 2; } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#processData(java.lang.String) */ @Override protected void processData(String line) throws ImporterException { String[] fields = line.split("\t"); List<GisFeature> gisFeatures = new ArrayList<GisFeature>(); // // Line table has the following fields : // --------------------------------------------------- //O : geonames id; 1 shape // checkNumberOfColumn(fields); //geonamesId if (!isEmptyField(fields, 0, false)) { String geonamesIds=fields[0].trim(); String[] ids = geonamesIds.split(","); for (String geonamesId:ids){ long geonamesIdAsLong; try { geonamesIdAsLong = Long.parseLong(geonamesId); } catch (NumberFormatException e) { logger.error("can not parse geonames id :"+geonamesId); //we don't want to parse when there is several ids (e.g:146390,146639) because it can cause error //todo if there is several ids search the rearest. continue; } GisFeature gisFeature = gisFeatureDao.getByFeatureId(geonamesIdAsLong); if (gisFeature != null){ gisFeatures.add(gisFeature); } else { logger.warn("can not find gisfeature for geonames id "+geonamesId); continue; } } } else { logger.warn("There is no geonames Id for "+dumpFields(fields)); return; } Geometry shape =null; if(!isEmptyField(fields, 1, false)){ try { shape = (Geometry) GeolocHelper.convertFromHEXEWKBToGeometry(fields[1]); } catch (RuntimeException e) { logger.warn("can not parse shape for id "+fields[1]+" : "+e); return; } } else { logger.warn("There is no shape for "+dumpFields(fields)); return; } for (GisFeature gisFeature:gisFeatures){ if (gisFeature.getShape()!=null){ //the quattroshape data are less relevant than openstreetmap one, we don't update the shape //if it is already filled continue; } if (gisFeatures.size()==1 || shapeContainsPoint(shape, gisFeature)){ //if more than one feature match, we look at the one included in the shape gisFeature.setShape(shape); if (gisFeature.getSource()==GISSource.GEONAMES){ gisFeature.setSource(GISSource.GEONAMES_QUATTRO); } else if (gisFeature.getSource() ==GISSource.GEONAMES_OSM){ gisFeature.setSource(GISSource.GEONAMES_OSM_QUATTRO); } if (gisFeature instanceof City){ ((City) gisFeature).setMunicipality(true);//force to be a municipality. } try { savecity(gisFeature); } catch (ConstraintViolationException e) { logger.error("Can not save "+dumpFields(fields)+"(ConstraintViolationException) we continue anyway but you should consider this",e); }catch (Exception e) { logger.error("Can not save "+dumpFields(fields)+" we continue anyway but you should consider this",e); } } } } private boolean shapeContainsPoint(Geometry shape, GisFeature gisFeature) { try { return gisFeature.getLocation().within(shape); } catch (Exception e) { logger.error("can not determine if a shape contains point for "+gisFeature); return false; } } /** * @param fields * The array to process * @return a string which represent a human readable string of the Array but without shape because it is useless in logs */ protected static String dumpFields(String[] fields) { String result = "["; for (int i=0;i<fields.length;i++) { if (i==1){ result= result+"THE_SHAPE;"; }else { result = result + fields[i] + ";"; } } return result + "]"; } void savecity(GisFeature gisFeature) { if (gisFeature!=null){ gisFeatureDao.save(gisFeature); } } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#shouldBeSkiped() */ @Override public boolean shouldBeSkipped() { return !importerConfig.isQuattroshapesImporterEnabled() || !importerConfig.isGeonamesImporterEnabled(); } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#setCommitFlushMode() */ @Override protected void setCommitFlushMode() { this.gisFeatureDao.setFlushMode(FlushMode.COMMIT); } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#shouldIgnoreComments() */ @Override protected boolean shouldIgnoreComments() { return true; } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.AbstractImporterProcessor#shouldIgnoreFirstLine() */ @Override protected boolean shouldIgnoreFirstLine() { return false; } /* (non-Javadoc) * @see com.gisgraphy.domain.geoloc.importer.IGeonamesProcessor#rollback() */ public List<NameValueDTO<Integer>> rollback() { List<NameValueDTO<Integer>> deletedObjectInfo = new ArrayList<NameValueDTO<Integer>>(); logger.info("reseting quattroshapes"); //TODO only cities that have source openstreetmap deletedObjectInfo .add(new NameValueDTO<Integer>(City.class.getSimpleName(), 0)); resetStatus(); return deletedObjectInfo; } @Override //TODO test protected void tearDown() { super.tearDown(); String savedMessage = this.statusMessage; try { this.statusMessage = internationalisationService.getString("import.fulltext.optimize"); } finally { // we restore message in case of error this.statusMessage = savedMessage; } } @Required public void setGisFeatureDao(IGisFeatureDao gisFeatureDao) { this.gisFeatureDao = gisFeatureDao; } }