一个根据当前经纬度求"附近的人/商品"的工具类

声明:

1.该工具类是基本胚子,没有所谓设计模式.但可以完美使用

2.该工具类附带有web监听器,供持久化存储

3.速度超快

代码:

public class LatData implements Serializable,Comparable<LatData>{
	/**
	 * 
	 */
	private static final long serialVersionUID = -2841897755569229115L;

	private double lat;
	
	private List<Object> list = new ArrayList<>();

	public double getLat() {
		return lat;
	}

	public void setLat(double lat) {
		this.lat = lat;
	}

	public List<Object> getList() {
		return list;
	}

	public void setList(List<Object> list) {
		this.list = list;
	}

	public LatData(double lat) {
		super();
		this.lat = lat;
	}
	public LatData(double lat,Object obj) {
		super();
		this.lat = lat;
		list.add(obj);
	}

	public LatData() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public int compareTo(LatData o) {
		if (this.getLat() < o.getLat()){
			return -1;
		}
		if (this.getLat() > o.getLat()){
			return 1;
		}
		return 0;
	}

	@Override
	public String toString() {
		return "LatData [lat=" + lat + ", list=" + list + "]";
	}
}
/**
 * 经度数据
 * @author 
 *
 * 2018年12月28日
 */
public class LonData implements Serializable,Comparable<LonData>{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1361788488326400849L;
	private double lon;	
	
	private TreeSet<LatData> lats = new TreeSet<>();

	public double getLon() {
		return lon;
	}
	public void setLon(double lon) {
		this.lon = lon;
	}
	
	public TreeSet<LatData> getLats() {
		return lats;
	}
	public void setLats(TreeSet<LatData> lats) {
		this.lats = lats;
	}
	public static LonData create(double lon){
		LonData lonObject = new LonData();
		lonObject.setLon(lon);
		return lonObject;
	}
	@Override
	public String toString() {
		return "LonData [lon=" + lon + ", lats=" + lats + "]";
	}
	@Override
	public int compareTo(LonData o) {
		if (this.getLon() < o.getLon()){
			return -1;
		}
		if (this.getLon() > o.getLon()){
			return 1;
		}
		return 0;
	}
}
/**
 * 查询返回数据
 * @author 
 *
 * 2018年12月28日
 */
public class Res implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -8340410745934084391L;

	private double lon;
	
	private double lat;

	
	private double distance;
	
	private Object object;
	
	
	
	
	public Object getObject() {
		return object;
	}

	public void setObject(Object object) {
		this.object = object;
	}

	public double getDistance() {
		return distance;
	}

	public void setDistance(double distance) {
		this.distance = distance;
	}

	public double getLon() {
		return lon;
	}

	public void setLon(double lon) {
		this.lon = lon;
	}

	public double getLat() {
		return lat;
	}

	public void setLat(double lat) {
		this.lat = lat;
	}

	
	
	

	public Res(double lon, double lat, Object object) {
		super();
		this.lon = lon;
		this.lat = lat;
		this.object = object;
	}

	public Res() {
		super();
		
	}

	public  double distance(double otherLon, double otherlat){
		distance = DistanceUtil.getGreatCircleDistance(lon, lat, otherLon, otherlat);
		return distance;
	}

	@Override
	public String toString() {
		return "Res [lon=" + lon + ", lat=" + lat + ", distance=" + distance + ", object=" + object + "]";
	}
}
/**
 * 计算经纬度和距离的工具类
 * @author 
 *
 * 2018年12月28日
 */
public class DistanceUtil implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 8747777023173566233L;
	// 地球半径
	public static final double EARTH_RADIUS = 6378137.0; // 单位M

	private static double getRad(double d) {
		return d * Math.PI / 180.0;
	}

	/**
	 * 两坐标(经纬度)之间距离计算 GD/BD地图采用此算法
	 * 
	 * @param startLng
	 *            起点坐标经度
	 * @param startLat
	 *            起点坐标纬度
	 * @param endLng
	 *            终点坐标经度
	 * @param endLat
	 *            终点坐标纬度
	 * @return 起点坐标与终点坐标的距离 单位:m
	 */
	public static double getGreatCircleDistance(double startLng, double startLat, double endLng, double endLat) {
		double radLat1 = getRad(startLat);
		double radLat2 = getRad(endLat);
		double dy = radLat1 - radLat2; // a
		double dx = getRad(startLng) - getRad(endLng); // b
		double s = 2 * Math.asin(Math.sqrt(
				Math.pow(Math.sin(dy / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(dx / 2), 2)));
		s = s * EARTH_RADIUS;
		s = Math.round(s * 10000) / 10000.0;
		return s;
	}

	public static void main(String[] args) {

		// 东经106.486654度 北纬23.490295度 向东移动4000米 向北移动3000米
		double lng = getMoveLng(23.490295, 4000) + 106.486654;
		double lat = getMoveLat(3000) + 23.490295;
		System.out.println(lat);
		System.out.println(lng);
		System.out.println("移动后两点距离(米):" + getGreatCircleDistance(106.486654, 23.490295, lng, lat));
	}

	/**
	 * 获取纬度移动latMovement米,纬度移动的度数
	 * 
	 * @param latMovement
	 *            单位:米
	 * @return 纬度移动的度数
	 */
	public static double getMoveLat(double latMovement) {
		return (180 / (Math.PI * EARTH_RADIUS)) * latMovement;
	}

	/**
	 * 获取经度移动lngMovement米,经度移动的度数
	 * 
	 * @param lat
	 *            纬度
	 * @param lngMovement
	 *            单位:米
	 * @return 经度移动的度数
	 */
	public static double getMoveLng(double lat, double lngMovement) {
		return (180 / (Math.PI * EARTH_RADIUS * Math.cos(Math.toRadians(lat)))) * lngMovement;
	}

}
/**
 * 供GEO数据持久化的监听器
 * 
 * @author 
 *
 *         2018年12月28日
 */
public class GeoLisener implements ServletContextListener {
	private static String fileName = "";

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		try (FileInputStream fis = new FileInputStream(fileName); 
				ObjectInputStream ois = new ObjectInputStream(fis);) {
			Object object = ois.readObject();
			if (object != null) {
				GeoUtil.lonSet = (TreeSet<LonData>) object;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		try(FileOutputStream fos = new FileOutputStream(fileName);
				ObjectOutputStream oos = new ObjectOutputStream(fos);){
			oos.writeObject(GeoUtil.lonSet);
		}catch (Exception e) {
			e.printStackTrace();
		}

	}

}
/**
 * 对外开放的使用类
 * @author 
 *
 * 2018年12月28日
 */
public class GeoUtil{
	
	
	
	
	public static void main(String[] args) {
		double lon  = 113.7704658508;
		double lat  = 34.7189693687;
		for (double i = 0; i < 0.001; i = i + 0.000001) {
			put(lon+i, lat+i,"呵呵");
		}
		put(lon, lat,"呵呵111");
		long currentTimeMillis = System.currentTimeMillis();
		System.out.println(search(lon, lat,10));
		System.out.println(System.currentTimeMillis()-currentTimeMillis);
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	public static TreeSet<LonData> lonSet = new TreeSet<LonData>();
	/**
	 * 查找某点多少米范围的所有点,是正方形范围
	 * @param lon 当前精度
	 * @param lat 当前纬度
	 * @param distance 单位米,范围
	 * @return
	 */
	public static TreeSet<Res> search(double lon,double lat,double distance){
		TreeSet<Res> resset = new TreeSet<Res>(new Comparator<Res>() {
			@Override
			public int compare(Res o1, Res o2) {
				if (o1.distance(lon, lat) < o2.distance(lon, lat)){
					return -1;
				}
				return 1;
			}
		});
		NavigableSet<LonData> set = lonSet.subSet(LonData.create(lon-DistanceUtil.getMoveLng(lat, distance)), true, LonData.create(lon+DistanceUtil.getMoveLng(lat, distance)), true);
		for (LonData lonObject : set) {
			NavigableSet<LatData> subSet = lonObject.getLats().subSet(new LatData(lat-DistanceUtil.getMoveLat(distance)), true, new LatData(lat+DistanceUtil.getMoveLat(distance)), true);
			for (LatData latData : subSet) {
				resset.add(new Res(lonObject.getLon(), latData.getLat(), latData.getList()));
			}
		}
		return resset;
	}
	
	public static void put(double lon,double lat,Object data){
		LonData create = LonData.create(lon);
		if (lonSet.contains(create)){
			//如果存在
			LonData ceiling = lonSet.ceiling(create);
			TreeSet<LatData> lats = ceiling.getLats();
			if (lats.contains(new LatData(lat))){
				LatData latData = lats.ceiling(new LatData(lat));
				latData.getList().add(data);
			}else {
				lats.add(new LatData(lat,data));
			}
			
		}else{
			//如果不存在
			create.getLats().add(new LatData(lat,data));
			lonSet.add(create);
		}
	}
	public static void remove(double lon,double lat){
		LonData create = LonData.create(lon);
		if (lonSet.contains(create)){
			//如果存在
			lonSet.ceiling(create).getLats().remove(new LatData(lat));
		}
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值