Lucene Spatial构建地理空间索引
一、Maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.sdu.lucene</groupId>
<artifactId>lucene-learn</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<lucene.version>6.1.0</lucene.version>
<spatial4j.version>0.6</spatial4j.version>
<guava.version>19.0</guava.version>
</properties> <dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency> <dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency> <!-- Lucene分词 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>${lucene.version}</version>
</dependency> <!-- 地理坐标支持 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-spatial</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-spatial-extras</artifactId>
<version>${lucene.version}</version>
</dependency> <!--
Spatial4j是一个通用的空间/地理空间ASL许可的开源Java库,它的核心能力有3个方面:
1 : 提供公共图形,可工作在Euclidean和geodesic(球体的表面)的世界模型
2 : 提供距离计算和其它数学计算
3 : 从WKT 格式化字符串来读取形状
-->
<dependency>
<groupId>org.locationtech.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>${spatial4j.version}</version>
</dependency>
</dependencies> <build>
<finalName>lucene-learn</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build> </project>
二、Lucene Spatial
/**
* @author hanhan.zhang
* */
public class LuceneSpatial { /**
* Spatial4j上下文
* 1: SpatialContext初始化可由SpatialContextFactory配置
* 2: SpatialContext属性
* DistanceCalculator(默认使用GeodesicSphereDistCalc.Haversine,将地球视为标准球体)
* ShapeFactory(默认使用ShapeFactoryImpl)
* Rectangle(构建经纬度空间:RectangleImpl(-180, 180, -90, 90, this))
* BinaryCodec()
* */
private SpatialContext ctx; /**
* 索引和查询模型的策略接口
* */
private SpatialStrategy strategy; /**
* 索引存储目录
* */
private Directory directory; protected void init() {
/**
* SpatialContext也可以通过SpatialContextFactory工厂类来构建
* */
this.ctx = SpatialContext.GEO; /**
* 网格最大11层或Geo Hash的精度
* 1: SpatialPrefixTree定义的Geo Hash最大精度为24
* 2: GeohashUtils定义类经纬度到Geo Hash值公用方法
* */
SpatialPrefixTree spatialPrefixTree = new GeohashPrefixTree(ctx, 11); /**
* 索引和搜索的策略接口,两个主要实现类
* 1: RecursivePrefixTreeStrategy(支持任何Shape的索引和检索)
* 2: TermQueryPrefixTreeStrategy(仅支持Point Shape)
* 上述两个类继承PrefixTreeStrategy(有使用缓存)
* */
this.strategy = new RecursivePrefixTreeStrategy(spatialPrefixTree, "location");
// 初始化索引目录
this.directory = new RAMDirectory();
} protected void createIndex(List<CityGeoInfo> cityGeoInfos) throws Exception {
IndexWriterConfig config = new IndexWriterConfig(new StandardAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, config);
indexWriter.addDocuments(newSampleDocument(ctx, strategy, cityGeoInfos));
indexWriter.close();
} /**
* 创建Document索引对象
*/
protected List<Document> newSampleDocument(SpatialContext ctx, SpatialStrategy strategy, List<CityGeoInfo> cityGeoInfos) {
List<Document> documents = Lists.newLinkedList(cityGeoInfos.stream()
.map(cgi -> {
Document doc = new Document();
doc.add(new StoredField("id", cgi.getCityId()));
doc.add(new NumericDocValuesField("id", cgi.getCityId()));
doc.add(new StringField("city", cgi.getName(), Field.Store.YES));
Shape shape = null;
/**
* 对小于MaxLevel的Geo Hash构建Field(IndexType[indexed,tokenized,omitNorms])
* */
Field []fields = strategy.createIndexableFields((shape = ctx.getShapeFactory()
.pointXY(cgi.getLnt(), cgi.getLat())));
for (Field field : fields) {
doc.add(field);
}
Point pt = (Point) shape;
doc.add(new StoredField(strategy.getFieldName(), pt.getX() + ","+ pt.getY()));
return doc;
})
.collect(Collectors.toList()));
return documents;
} /**
* 地理位置搜索
* @throws Exception
*/
public void search() throws Exception {
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
/**
* 按照id升序排序
* */
Sort idSort = new Sort(new SortField("id", SortField.Type.INT)); /**
* 搜索方圆100千米范围以内,以当前位置经纬度(120.33,36.07)青岛为圆心,其中半径为100KM
* */
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects,
ctx.getShapeFactory().circle(120.33, 36.07, DistanceUtils.dist2Degrees(100, DistanceUtils.EARTH_MEAN_RADIUS_KM)));
Query query = strategy.makeQuery(args);
TopDocs topDocs = indexSearcher.search(query, 10, idSort);
/**
* 输出命中结果
* */
printDocument(topDocs, indexSearcher, args.getShape().getCenter()); System.out.println("==========================华丽的分割线========================="); /**
* 定义坐标点(x,y)即(经度,纬度)即当前用户所在地点(烟台)
* */
Point pt = ctx.getShapeFactory().pointXY(121.39,37.52); /**
* 计算当前用户所在坐标点与索引坐标点中心之间的距离即当前用户地点与每个待匹配地点之间的距离,DEG_TO_KM表示以KM为单位
* 对Field(name=location)字段检索
* */
ValueSource valueSource = strategy.makeDistanceValueSource(pt, DistanceUtils.DEG_TO_KM); /**
* 根据命中点与当前位置坐标点的距离远近降序排,距离数字大的排在前面,false表示降序,true表示升序
* */
Sort distSort = new Sort(valueSource.getSortField(false)).rewrite(indexSearcher);
TopDocs topdocs = indexSearcher.search(new MatchAllDocsQuery(), 10, distSort);
printDocument(topdocs, indexSearcher, pt);
indexReader.close();
} protected void printDocument(TopDocs topDocs, IndexSearcher indexSearcher, Point point) throws Exception {
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
Document document = indexSearcher.doc(docId);
int cityId = document.getField("id").numericValue().intValue();
String city = document.getField("city").stringValue();
String location = document.getField(strategy.getFieldName()).stringValue();
String []locations = location.split(",");
double xPoint = Double.parseDouble(locations[0]);
double yPoint = Double.parseDouble(locations[1]);
double distDEG = ctx.calcDistance(point, xPoint, yPoint);
double juli = DistanceUtils.degrees2Dist(distDEG, DistanceUtils.EARTH_MEAN_RADIUS_KM);
System.out.println("docId=" + docId + "\tcityId=" + cityId + "\tcity=" + city + "\tdistance=" + juli + "KM");
}
} public static void main(String[] args) throws Exception {
LuceneSpatial luceneSpatial = new LuceneSpatial();
luceneSpatial.init();
luceneSpatial.createIndex(GeoHelper.getCityGeoInfo("/Users/hanhan.zhang/Downloads/geo.txt"));
luceneSpatial.search();
} }
三、地理信息文件
招远: 120.38,37.35
舟山: 122.207216,29.985295
齐齐哈尔:123.97,47.33
盐城: 120.13,33.38
赤峰: 118.87,42.28
青岛: 120.33,36.07
乳山: 121.52,36.89
金昌: 102.188043,38.520089
泉州: 118.58,24.93
莱西: 120.53,36.86
日照: 119.46,35.42
胶南: 119.97,35.88
南通: 121.05,32.08
拉萨: 91.11,29.97
云浮: 112.02,22.93
梅州: 116.1,24.55
文登: 122.05,37.2
上海: 121.48,31.22
攀枝花: 101.718637,26.582347
威海: 122.1,37.5
承德: 117.93,40.97
厦门: 118.1,24.46
汕尾: 115.375279,22.786211
潮州: 116.63,23.68
丹东: 124.37,40.13
太仓: 121.1,31.45
曲靖: 103.79,25.51
烟台: 121.39,37.52
福州: 119.3,26.08
瓦房店: 121.979603,39.627114
即墨: 120.45,36.38
抚顺: 123.97,41.97
玉溪: 102.52,24.35
张家口: 114.87,40.82
阳泉: 113.57,37.85
莱州: 119.942327,37.177017
湖州: 120.1,30.86
汕头: 116.69,23.39
昆山: 120.95,31.39
宁波: 121.56,29.86
湛江: 110.359377,21.270708
揭阳: 116.35,23.55
荣成: 122.41,37.16
连云港: 119.16,34.59
葫芦岛: 120.836932,40.711052
常熟: 120.74,31.64
东莞: 113.75,23.04
河源: 114.68,23.73
淮安: 119.15,33.5
泰州: 119.9,32.49
南宁: 108.33,22.84
营口: 122.18,40.65
惠州: 114.4,23.09
江阴: 120.26,31.91
蓬莱: 120.75,37.8
韶关: 113.62,24.84
嘉峪关: 98.289152,39.77313
广州: 113.23,23.16
延安: 109.47,36.6
太原: 112.53,37.87
清远: 113.01,23.7
中山: 113.38,22.52
昆明: 102.73,25.04
寿光: 118.73,36.86
盘锦: 122.070714,41.119997
长治: 113.08,36.18
深圳: 114.07,22.62
珠海: 113.52,22.3
宿迁: 118.3,33.96
咸阳: 108.72,34.36
铜川: 109.11,35.09
平度: 119.97,36.77
佛山: 113.11,23.05
海口: 110.35,20.02
江门: 113.06,22.61
章丘: 117.53,36.72
肇庆: 112.44,23.05
大连: 121.62,38.92
临汾: 111.5,36.08
吴江: 120.63,31.16
石嘴山: 106.39,39.04
沈阳: 123.38,41.8
苏州: 120.62,31.32
茂名: 110.88,21.68
嘉兴: 120.76,30.77
长春: 125.35,43.88
胶州: 120.03336,36.264622
银川: 106.27,38.47
张家港: 120.555821,31.875428
三门峡: 111.19,34.76
锦州: 121.15,41.13
南昌: 115.89,28.68
柳州: 109.4,24.33
三亚: 109.511909,18.252847
自贡: 104.778442,29.33903
吉林: 126.57,43.87
阳江: 111.95,21.85
泸州: 105.39,28.91
西宁: 101.74,36.56
宜宾: 104.56,29.77
呼和浩特:111.65,40.82
成都: 104.06,30.67
大同: 113.3,40.12
镇江: 119.44,32.2
桂林: 110.28,25.29
张家界: 110.479191,29.117096
宜兴: 119.82,31.36
北海: 109.12,21.49
西安: 108.95,34.27
金坛: 119.56,31.74
东营: 118.49,37.46
牡丹江: 129.58,44.6
遵义: 106.9,27.7
绍兴: 120.58,30.01
扬州: 119.42,32.39
常州: 119.95,31.79
潍坊: 119.1,36.62
重庆: 106.54,29.59
台州: 121.420757,28.656386
南京: 118.78,32.04
滨州: 118.03,37.36
贵阳: 106.71,26.57
无锡: 120.29,31.59
本溪: 123.73,41.3
克拉玛依:84.77,45.59
渭南: 109.5,34.52
马鞍山: 118.48,31.56
宝鸡: 107.15,34.38
焦作: 113.21,35.24
句容: 119.16,31.95
北京: 116.46,39.92
徐州: 117.2,34.26
衡水: 115.72,37.72
包头: 110,40.58
绵阳: 104.73,31.48
乌鲁木齐:87.68,43.77
枣庄: 117.57,34.86
杭州: 120.19,30.26
淄博: 118.05,36.78
鞍山: 122.85,41.12
溧阳: 119.48,31.43
库尔勒: 86.06,41.68
安阳: 114.35,36.1
开封: 114.35,34.79
济南: 117,36.65
德阳: 104.37,31.13
温州: 120.65,28.01
九江: 115.97,29.71
邯郸: 114.47,36.6
临安: 119.72,30.23
兰州: 103.73,36.03
沧州: 116.83,38.33
临沂: 118.35,35.05
南充: 106.110698,30.837793
天津: 117.2,39.13
富阳: 119.95,30.07
泰安: 117.13,36.18
诸暨: 120.23,29.71
郑州: 113.65,34.76
哈尔滨: 126.63,45.75
聊城: 115.97,36.45
芜湖: 118.38,31.33
唐山: 118.02,39.63
平顶山: 113.29,33.75
邢台: 114.48,37.05
德州: 116.29,37.45
济宁: 116.59,35.38
荆州: 112.239741,30.335165
宜昌: 111.3,30.7
义乌: 120.06,29.32
丽水: 119.92,28.45
洛阳: 112.44,34.7
秦皇岛: 119.57,39.95
株洲: 113.16,27.83
石家庄: 114.48,38.03
莱芜: 117.67,36.19
常德: 111.69,29.05
保定: 115.48,38.85
湘潭: 112.91,27.87
金华: 119.64,29.12
岳阳: 113.09,29.37
长沙: 113,28.21
衢州: 118.88,28.97
廊坊: 116.7,39.53
菏泽: 115.480656,35.23375
合肥: 117.27,31.86
武汉: 114.31,30.52
大庆: 125.03,46.5
Lucene Spatial构建地理空间索引的更多相关文章
- MongoDB-JAVA-Driver 3.2版本常用代码全整理(4) - 地理空间索引
MongoDB的3.x版本Java驱动相对2.x做了全新的设计,类库和使用方法上有很大区别.例如用Document替换BasicDBObject.通过Builders类构建Bson替代直接输入$命令等 ...
- 全文索引&&地理空间索引
Ⅰ.全文索引 搜索引擎的实现核心技术,搜索类似where col like '%xxx%';关键字可以出现再某个列任何位置 这种查询条件,B+ tree索引是无法使用的.如果col上创建了索引,因为排 ...
- 玩转mongodb(七):索引,速度的引领(全文索引、地理空间索引)
本篇博文主要介绍MongoDB中一些常用的特殊索引类型,主要包括: 用于简单字符串搜索的全文本索引: 用于球体空间(2dsphere)和二维平面(2d)的地理空间索引. 一.全文索引 MongoDB有 ...
- Mongodb地理空间索引
1.索引: 建立索引既耗时也费力,还需要消耗很多资源.使用{"bakckground":true}选项可以使这个过程在后台完成,同时正常处理请求.如果不包括background 这 ...
- mongodb的地理空间索引常见的问题
创建地理空间索引注意事项 创建地理空间索引失败,提示错误信息如下 > db.places.ensureIndex({"loc":"2dsphere"}){ ...
- Lucene核心--构建Lucene搜索(下篇,理论篇)
2.1.6 截取索引(Indextruncate) 一些应用程序的所以文档的大小先前是不知道的.作为控制RAM和磁盘存储空间的使用数量的安全机制,你可能想要限制每个字段允许输入索引的输入数量.一个大的 ...
- Lucene核心--构建Lucene搜索(上篇,理论篇)
2.1构建Lucene搜索 2.1.1 Lucene内容模型 一个文档(document)就是Lucene建立索引和搜索的原子单元,它由一个或者多个字段(field)组成,字段才是Lucene的真实内 ...
- MongoDB系列五(地理空间索引与查询).
一.经纬度表示方式 MongoDB 中对经纬度的存储有着自己的一套规范(主要是为了可以在该字段上建立地理空间索引).包括两种方式,分别是 Legacy Coordinate Pairs (这个词实在不 ...
- Oracle Spatial中的空间索引
转自cryolite原文 Oracle Spatial中的空间索引 Oracle Spatial可对空间数据进行R-tree索引,每个空间图层(Spatial Layer)的空间索引元信息都可以在US ...
随机推荐
- 如何严格设置php中session过期时间 (转)
如何严格限制session在30分钟后过期!1.设置客户端cookie的lifetime为30分钟:2.设置session的最大存活周期也为30分钟:3.为每个session值加入时间戳,然后在程序调 ...
- Codeforces 1099 D. Sum in the tree-构造最小点权和有根树 贪心+DFS(Codeforces Round #530 (Div. 2))
D. Sum in the tree time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- Sqli-labs less 7
Less-7 本关的标题是dump into outfile,意思是本关我们利用文件导入的方式进行注入.而在background-3中我们已经学习了如何利用dump into file. 这里首先还是 ...
- 进入CentOS7紧急模式恢复root密码
第一步.重启CentOS7,在以下界面选择要编辑的内核(一般第一个),按e进入编辑界面 第二步.在编辑界面找到如下一行,将ro改为rw init=/sysroot/bin/sh.改完后<Ctrl ...
- 多进程失败拉起的demo
#include <iostream> #include <vector> #include <unistd.h> #include <stdlib.h> ...
- POJ1704 Georgia and Bob 博弈论 尼姆博弈 阶梯博弈
http://poj.org/problem?id=1704 我并不知道阶梯博弈是什么玩意儿,但是这道题的所有题解博客都写了这个标签,所以我也写了,百度了一下,大概是一种和这道题类似的能转换为尼姆博弈 ...
- BZOJ 2152 聪聪可可(点分治)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2152 [题目大意] 给出一棵树,问任取两点之间距离为3的倍数的概率是多少 [题解] 树 ...
- 【点分治】Osipovsky Cup 2014 Kovrov, Sunday, December 21, 2014 Problem A. Attack and Defence
题意:给你一棵树,每个点有一个左括号或者右括号,问你树上能够完美匹配的路径数量(l->r,r->l 视作不同路径). 点分治可以使用“不扣去重复答案”的写法,只不过,要先将每个点的子树按照 ...
- 【计算几何】【凸包】Gym - 101164H - Pub crawl
平面上n个点,点之间沿直线走,规划一条路线,每次只能往左半平面的点走,走过最多的点. 显然所有的点都能走过. n^2的暴力显然是每次找左边与其所形成夹角最小的点,但这样过不了(卡常数?). 或者每轮不 ...
- python学习第九十天:vue补习2
Vue 八.重要指令 v-bind <!-- 值a --> <div v-bind:class='"a"'></div> <!-- 变量a ...