Spatial4j+ForkJoin 实现经纬度距离计算并排序
<dependency>
<groupId>com.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>0.5</version>
</dependency>
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceUtils;
import com.spatial4j.core.shape.Rectangle;
/**
* java类简单作用描述
*
* @Package: cn.fulong.bjwalk.findhome.util
* @Author: yu_du_chen
* @CreateDate: 2020/8/29 10:43
* @Version: 1.0
*/
public class spatial {
// 移动设备经纬度
private double lon;
private double lat;
//千米
final private static int radius = 16;
public spatial() {
this(116.311777,40.035986);
}
public spatial(double lon, double lat) {
this.lon = lon;
this.lat = lat;
System.out.println("配置加载");
}
/**根据设置的范围 检索*/
public void xyRange(){
System.out.println(lon);
System.out.println(lat);
SpatialContext geo = SpatialContext.GEO;
Rectangle rectangle = geo.getDistCalc().calcBoxByDistFromPt(
geo.makePoint(lon, lat), radius * DistanceUtils.KM_TO_DEG, geo, null);
System.out.println(rectangle.getMinX() + "-" + rectangle.getMaxX());// 经度范围
System.out.println(rectangle.getMinY() + "-" + rectangle.getMaxY());// 纬度范围
}
/**商户经纬度和当前经纬度距离,传入商户经纬度*/
public double xyDistance(double lon2,double lat2){
SpatialContext geo = SpatialContext.GEO;
double distance = geo.calcDistance(geo.makePoint(lon, lat), geo.makePoint(lon2, lat2))
* DistanceUtils.DEG_TO_KM;
return distance;
}
public static void main(String[] args) {
spatial A = new spatial();
A.xyRange();
A.xyDistance(116.426612,39.905184);//北京站地铁站-A口
}
}
ForkJoin的用法可参考 : ForkJoin
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.RecursiveTask;
/**
* ForkJoin 多任务
* @ProjectName: utils
* @Package: cn.fulong.web.forkjoin
* @Author: yu_du_chen
* @CreateDate: 2019/9/10 16:26
* @Version: 1.0
*/
@Component
public class ForkJoin{
/**
* 单个排序的子任务
*/
public static class MyTask extends RecursiveTask<CopyOnWriteArrayList> {
private CopyOnWriteArrayList<Map<String, Object>> maps;
private spatial A;
public MyTask(List<Map<String, Object>> maps,spatial A) {
this.maps = new CopyOnWriteArrayList(maps);
this.A = A;
}
/* (non-Javadoc)
* @see java.util.concurrent.RecursiveTask#compute()
*/
@Override
protected CopyOnWriteArrayList compute() {
int sourceLen = maps.size();
if(sourceLen > 1) {
int midIndex = sourceLen / 2;
// 拆分成两个子任务
MyTask task1 = new MyTask(maps.subList(0, midIndex),A);
task1.fork();
MyTask task2 = new MyTask(maps.subList(midIndex, sourceLen),A);
task2.fork();
// 结果集合并
CopyOnWriteArrayList list1 = task1.join();
CopyOnWriteArrayList list2 = task2.join();
list1.addAll(list2);
return list1;
}else {
double a = A.xyDistance(Double.parseDouble(maps.get(0).get("position_x").toString()),Double.parseDouble(maps.get(0).get("position_y").toString()));
//计算出的距离反存入 list
maps.get(0).put("distance",a);
return maps;
}
}
}
}
/*per 分页、school查询参数、x/y移动设备当前经纬度、sortFlag升序/降序*/
@Override
public Map<String, Object> getCloudClassList(Integer per, School school, Double x, Double y, boolean sortFlag) {
//MyBatis-Plus的查询用法
IPage<School> iPage = new Page<>(Optional.ofNullable(per).orElse(0), FindConstants.SchollList);
IPage<Map<String, Object>> list = schoolMapper.selectMapsPage(iPage,Wrappers.<School>lambdaQuery()
.like(IsNotNull.get(school.getSchoolName()),School::getSchoolName, school.getSchoolName())
.eq(IsNotNull.get(school.getId()),School::getId, school.getId())
.eq(true,School::getFlag, "1"));
System.out.println("总页数:" + list.getPages());
System.out.println("总数:" + list.getTotal());
System.out.println("页数:" + list.getSize());
System.out.println("页码:" + list.getCurrent());
//业务需求 前端需要这些参数 放入map中
//list.getRecords() 查询出的所有学校数据
Map<String, Object> map = new LinkedHashMap();
map.put("pages",list.getPages());
map.put("total",list.getTotal());
map.put("size",list.getSize());
map.put("current",list.getCurrent());
//IsNotNull 自封装的判空方法 后面会给出文章链接
if(IsNotNull.get(list.getRecords())){
/*开启任务*/
/*方法实现的是:通过spatial4j计算出 list每条数据距离当前移动设备的距离(distance)反存入list,
并且按照distance排序*/
ForkJoinPool pool = new ForkJoinPool();
ForkJoin.MyTask task = new ForkJoin.MyTask(list.getRecords(),new spatial(x,y));
ForkJoinTask<CopyOnWriteArrayList> index = pool.submit(task);
try {
CopyOnWriteArrayList<Map<String, Object>> m = index.get();
//通过distance排序 具体用法 参考java1.8 stream().sorted 这里不做过多解释
List<Map<String, Object>> nameList = m.stream().sorted((a,b) -> getCollect(sortFlag,a,b)).collect(Collectors.toList());
Map<String, Object> map1 = new LinkedHashMap();
map1.put("data",nameList);
map1.put("ipage",map);
return map1;
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
return null;
}
private int getCollect(boolean sortFlag, Map<String, Object> a, Map<String, Object> b){
if(sortFlag){
if (b.get("distance").toString().equals(a.get("distance").toString())) {
return b.get("id").toString().compareTo(a.get("id").toString());
}else {
return new BigDecimal(b.get("distance").toString()).compareTo(new BigDecimal(a.get("distance").toString()));
}
}else{
if (a.get("distance").toString().equals(b.get("distance").toString())) {
return a.get("id").toString().compareTo(b.get("id").toString());
}else {
return new BigDecimal(a.get("distance").toString()).compareTo(new BigDecimal(b.get("distance").toString()));
}
}
}