空间关系
有时业务里需要显示特定范围的数据(点, 线, 面等, 一般都是点, 比如警情五公里内的监控探头), 具体的如下图:
查询在特定范围内的数据我们称之为 空间查询
总体原则
- 数据量大要在服务端做
- 数据量小的可以在前端做, 数据量一般不超过1w条
- 查询的图形一般为圆或者任意多边形
前端查询
就是一次性加载所有的数据, 然后计算哪些数据落在这个图形边界内, 点在圆内,点在多边形内等. 常用的库有:
业务很简单的情况下推荐使用 geolib
,因为其很简单和小巧,比如查询圆内的落点, 针对 turfjs
我们也做了个简单的封装, 详情请查看 插件目录里的yymap.turf
简单的示例代码:
js
const center = [120, 31], //圆的中心点
radius = 1000; //半径
//geolib
const result = data.filter(d => {
return YY.geolib.getDistance(d.lnglat, center) <= radius;
});
// result 就是圆内的数据
//turfjs
const centerPoint = new YY.Point(center);
const result = data.filter(d => {
return YY.Turf.distance(new YY.Point(d.lnglats), centerPoint) <= radius;
})
警告
再次强调该方法仅适用于数据量小的情况
服务端查询
服务端地理数据常见的存储方式:
- 数据库直接存地理数据(推荐):数据库层面一般数据库都支持
空间查询
, 比如PostgreSQL(PostGIS),SQL SERVER
,ORCALDB
等。 数据库层面的空间查询
需要你的数据以空间数据结构的方式存储, 如果你的数据就是这样存储的, 那么你可以直接利用数据库提供的原生函数来进行空间查询
警告
如果你的业务强烈的这种需求,推荐PostgreSQL(PostGIS) 作为你们的数据库
- elasticsearch 直接存地理数据: 如果你的业务里有使用elasticsearch, 你也可以使用elasticsearch来存储地理数据并提供查询服务query shape
警告
注意不能为了用而用, 根据自己的业务的真实需求进行技术选型, 毕竟数据库和elasticsearch的数据同步也是个头疼问题
- 以普通的关系数据库存数据:可能都是因为一些历史原因导致的, 或者你的业务不是强GIS, 即使这么做也可以完成业务需求, 这个存储方式需要搭配 JTS 来进行空间计算
警告
如果你的业务数据是 点
数据请把经纬度拆开存储, 比如分成两个字段 lng
, lat
, 方面在 SQL
层面根据范围来查询,这样就不用查询所有数据了和业务里进行大量的空间关系计算了
sql
//示例代码
select * from tablename wheren lng>=minx and lng<= maxx and lat>=miny and lat<=maxy
服务端常用的工具库有:
各种方案对比表:
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
数据库直接存地理数据 | 数据库直接原生支持空间查询, 无需写业务代码, 直接在SQL层面就可以了 | 业务里对数据库的增删改查都需要构造地理结构, 相对来说业务工作量变大了 | 常规需求, 一般系统都是这么设计和技术选型 |
elasticsearch直接存地理数据 | elasticsearch支持空间查询, 且速度比较快 | 需要维护一个elasticsearch, 还有elasticsearch和数据库数据同步的问题 | 大规模的地理数据查询服务 |
以普通的关系数据库存数据 | 数据的存储比较简单,业务代码比较简单, 无需要构造各种地理结构进行增删改查 | 一般都需要把所有数读入内存来进行计算, 数据量大了,业务里容易把数据库拖死或者业务里内存溢出 | 地理数据服务不强, 数据量不大的情况 |
服务端接口设计上
一般是提供 lnglats
, radius
两个参数,分别表示中心点和半径, 前端提供这两个参数给服务端,服务端返回对应的结果给前端(点在圆内,点在多边形内等.)
js
ip: port / xxxxx / xxx ? lnglats = 120, 31 & radius = 5
建议
TIP
建议空间数据最好存在空间数据库里, 当然如果你的业务不是强GIS的或者因为一些其他的原因等还是保留原来的方式, 可能原因:
- 业务里对地理数据需求不强烈,简单的数据存储已经能满足业务需求
- 由于一些历史原因和兼容性不能随便切换数据库