Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Implementing Geospatial Bounding Box Search in Lucene 4.x

DZone's Guide to

Implementing Geospatial Bounding Box Search in Lucene 4.x

· Big Data Zone ·
Free Resource

Hortonworks Sandbox for HDP and HDF is your chance to get started on learning, developing, testing and trying out new features. Each download comes preconfigured with interactive tutorials, sample data and developments from the Apache community.

In this article I am going to explain how to implement GeoSpatial Bounding Box Search using Lucene 4.x.

Problem I am trying to Implement :

 Finding All Places Within Given Bounding Box.

Sample Input Places Data :

 	id  name             latitude   longitude
       
        //Bangalore places
        1   Bangalore        12.9558    77.620979
        2   Cubbon Park      12.974045  77.591995      
        3   Tipu Palace      12.959365  77.573792
        4   Bangalore Palace 12.998095  77.592041
        5   Monkey Bar       12.97018   77.61219

        //Chennai places
        6   Chennai    13.060422    80.249583
        7   Elliot's Beach    12.998976    80.271286
        8   Kapaleeshwar Temple    13.033889    80.269722
Code : 

import java.io.File;
import java.io.IOException;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;

import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceUtils;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;


public class SpatialSearch {
 
 private IndexWriter indexWriter;
 private IndexReader indexReader;
 private IndexSearcher searcher;
 private SpatialContext ctx;
 private SpatialStrategy strategy;
 
 public SpatialSearch(String indexPath) {
  
  StandardAnalyzer a = new StandardAnalyzer(Version.LUCENE_43);
  IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_43, a);
  Directory directory;
  
  try {
   directory = new SimpleFSDirectory(new File(indexPath));
   indexWriter = new IndexWriter(directory, iwc);
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
  this.ctx = SpatialContext.GEO;
  
  SpatialPrefixTree grid = new GeohashPrefixTree(ctx, 11);
  this.strategy = new RecursivePrefixTreeStrategy(grid, "location");
 }
 
 public void indexDocuments() throws IOException {
  
  indexWriter.addDocument(newGeoDocument(1, "Bangalore", ctx.makePoint(12.9558, 77.620979)));
  indexWriter.addDocument(newGeoDocument(2, "Cubbon Park", ctx.makePoint(12.974045, 77.591995)));
  indexWriter.addDocument(newGeoDocument(3, "Tipu palace", ctx.makePoint(12.959365, 77.573792)));
  indexWriter.addDocument(newGeoDocument(4, "Bangalore palace", ctx.makePoint(12.998095, 77.592041)));
  indexWriter.addDocument(newGeoDocument(5, "Monkey Bar", ctx.makePoint(12.97018, 77.61219)));
 indexWriter.addDocument(newGeoDocument(6, "Chennai", ctx.makePoint(13.060422, 80.249583)));
 indexWriter.addDocument(newGeoDocument(7, "Elliot's Beach", ctx.makePoint(12.998976, 80.271286))); 
 indexWriter.addDocument(newGeoDocument(8, "Kapaleeshwar Temple", ctx.makePoint(13.033889, 80.269722)));

  
  indexWriter.commit();
  indexWriter.close();
 }
 

 private Document newGeoDocument(int id, String name, Shape shape) {

  FieldType ft = new FieldType();
  ft.setIndexed(true);
  ft.setStored(true);

  Document doc = new Document();
  
  doc.add(new IntField("id", id, Store.YES));
  doc.add(new Field("name", name, ft));
  for(IndexableField f:strategy.createIndexableFields(shape)) {
   doc.add(f);
  }
  
  doc.add(new StoredField(strategy.getFieldName(), ctx.toString(shape)));
  
  return doc;
 }
 
 public void setSearchIndexPath(String indexPath) throws IOException{
  this.indexReader = DirectoryReader.open(new SimpleFSDirectory(new File(indexPath)));
  this.searcher = new IndexSearcher(indexReader);
 }
 
public void searchBBox(Double minLat, Double minLng, Double maxLat, Double maxLng) throws IOException {

   SpatialArgs args = new SpatialArgs(SpatialOperation.IsWithin, ctx.makeRectangle(minLat, maxLat, minLng, maxLng));

   Filter filter = strategy.makeFilter(args);
   int limit = 10; 
   TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), filter, limit);

   ScoreDoc[] scoreDocs = topDocs.scoreDocs; 
   for (ScoreDoc s : scoreDocs) { 
       Document doc = searcher.doc(s.doc); 
       System.out.println(doc.get("id") + "\t" + doc.get("name")); 
   } 
 }
  
}

 /**
  * @param args
  * @throws IOException 
  */
 public static void main(String[] args) throws IOException {
  // TODO Auto-generated method stub
  
  String indexPath = "/home/vishnu/lucene_practices/geo_spatial_index";
  
  SpatialSearch s = new SpatialSearch(indexPath);
  
  //Indexes sample documents
  s.indexDocuments();
  s.setSearchIndexPath(indexPath);
  
 //Get Places Within Chennai Bounding Box. 
 System.out.println("Places WithIn Chennai Bounding Box\n"); 
 s.searchBBox(12.9673, 80.184631, 13.15148, 80.306709); 


 //Get Places Within Bangalore Bounding Box.
 System.out.println("Places WithIn Bangalore Bounding Box"); 
 s.searchBBox(12.76805, 77.465202, 13.14355, 77.776749);

 }

}



Output :

Places WithIn Chennai Bounding Box
6 Chennai
7 Elliot's Beach
8 Kapaleeshwar Temple

Places WithIn Bangalore Bounding Box
1 Bangalore
2 Cubbon Park
3 Tipu palace
4 Bangalore palace
5 Monkey Bar


Hortonworks Community Connection (HCC) is an online collaboration destination for developers, DevOps, customers and partners to get answers to questions, collaborate on technical articles and share code examples from GitHub.  Join the discussion.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}