/** * Copyright (c) Codice Foundation * <p/> * This 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 3 of the * License, or any later version. * <p/> * This program 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. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. **/ package org.codice.ddf.spatial.geocoding.query; import java.io.IOException; import java.nio.file.Paths; import java.text.ParseException; import java.util.List; import java.util.Optional; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.codice.ddf.spatial.geocoding.GeoEntry; import org.codice.ddf.spatial.geocoding.GeoEntryQueryException; import org.codice.ddf.spatial.geocoding.context.NearbyLocation; import org.codice.ddf.spatial.geocoding.index.GeoNamesLuceneIndexer; import org.locationtech.spatial4j.context.SpatialContext; import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; import org.locationtech.spatial4j.shape.Shape; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GeoNamesQueryLuceneDirectoryIndex extends GeoNamesQueryLuceneIndex { private static final Logger LOGGER = LoggerFactory.getLogger(GeoNamesQueryLuceneDirectoryIndex.class); private static final JtsSpatialContextFactory JTS_SPATIAL_CONTEXT_FACTORY = new JtsSpatialContextFactory(); static { // permits geometry collections with intersecting polygons JTS_SPATIAL_CONTEXT_FACTORY.allowMultiOverlap = true; } private static final SpatialContext SPATIAL_CONTEXT = JTS_SPATIAL_CONTEXT_FACTORY.newSpatialContext(); private String indexLocation; public void setIndexLocation(final String indexLocation) { this.indexLocation = indexLocation; } @Override protected Directory openDirectory() throws IOException { return FSDirectory.open(Paths.get(indexLocation)); } private Directory openDirectoryAndCheckForIndex() throws GeoEntryQueryException { Directory directory; try { directory = openDirectory(); if (!indexExists(directory)) { directory.close(); LOGGER.debug( "There is no index at {}. Load a Geonames file into the offline gazetteer", indexLocation); return null; } return directory; } catch (IOException e) { throw new GeoEntryQueryException( "Error opening the index directory at " + indexLocation, e); } } @Override protected IndexReader createIndexReader(final Directory directory) throws IOException { return DirectoryReader.open(directory); } @Override protected IndexSearcher createIndexSearcher(final IndexReader indexReader) { final IndexSearcher indexSearcher = new IndexSearcher(indexReader); indexSearcher.setSimilarity(GeoNamesLuceneIndexer.SIMILARITY); return indexSearcher; } @Override public List<GeoEntry> query(final String queryString, final int maxResults) throws GeoEntryQueryException { final Directory directory = openDirectoryAndCheckForIndex(); return doQuery(queryString, maxResults, directory); } @Override public List<NearbyLocation> getNearestCities(final String location, final int radiusInKm, final int maxResults) throws ParseException, GeoEntryQueryException { if (location == null) { throw new IllegalArgumentException( "GeoNamesQueryLuceneDirectoryIndex.getNearestCities(): argument 'location' may not be null."); } Shape shape = getShape(location); final Directory directory = openDirectoryAndCheckForIndex(); return doGetNearestCities(shape, radiusInKm, maxResults, directory); } static Shape getShape(String location) throws ParseException { return SPATIAL_CONTEXT.readShapeFromWkt(location); } @Override public Optional<String> getCountryCode(String wktLocation, int radius) throws GeoEntryQueryException, ParseException { final Directory directory = openDirectoryAndCheckForIndex(); Shape shape = getShape(wktLocation); String countryCode = doGetCountryCode(shape, radius, directory); return Optional.ofNullable(countryCode); } }