zoukankan      html  css  js  c++  java
  • 查找附近网点geohash算法及实现 (Java版本号)

    參考文档:

    http://blog.csdn.net/wangxiafghj/article/details/9014363geohash  算法原理及实现方式
    http://blog.charlee.li/geohash-intro/  geohash:用字符串实现附近地点搜索
    http://blog.sina.com.cn/s/blog_7c05385f0101eofb.html    查找附近点--Geohash方案讨论
    http://www.wubiao.info/372        查找附近的xxx 球面距离以及Geohash方案探讨
    http://en.wikipedia.org/wiki/Haversine_formula       Haversine formula球面距离公式
    http://www.codecodex.com/wiki/Calculate_Distance_Between_Two_Points_on_a_Globe   球面距离公式代码实现
    http://developer.baidu.com/map/jsdemo.htm#a6_1   球面距离公式验证  
    http://www.wubiao.info/470     Mysql or Mongodb LBS高速实现方案


    geohash有下面几个特点:

    首先,geohash用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同一时候应用索引 (比如MySQL 4之前的版本号,Google App Engine的数据层等),利用geohash。仅仅需在一列上应用索引就可以。

    其次,geohash表示的并非一个点。而是一个矩形区域。比方编码wx4g0ec19,它表示的是一个矩形区域。 使用者能够公布地址编码,既能表明自己位于北海公园附近。又不至于暴露自己的精确坐标,有助于隐私保护。

    第三,编码的前缀能够表示更大的区域。比如wx4g0ec1。它的前缀wx4g0e表示包括编码wx4g0ec1在内的更大范围。 这个特性能够用于附近地点搜索。首先依据用户当前坐标计算geohash(比如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),就可以查询附近的全部地点。

    Geohash比直接用经纬度的高效非常多。

    Geohash算法实现(Java版本号)

    package com.DistTest;
    import java.util.BitSet;
    import java.util.HashMap;
    
    public class Geohash {
    
            private static int numbits = 6 * 5;
            final static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
                            '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p',
                            'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
           
            final static HashMap<Character, Integer> lookup = new HashMap<Character, Integer>();
            static {
                    int i = 0;
                    for (char c : digits)
                            lookup.put(c, i++);
            }
    
            public double[] decode(String geohash) {
                    StringBuilder buffer = new StringBuilder();
                    for (char c : geohash.toCharArray()) {
    
                            int i = lookup.get(c) + 32;
                            buffer.append( Integer.toString(i, 2).substring(1) );
                    }
                   
                    BitSet lonset = new BitSet();
                    BitSet latset = new BitSet();
                   
                    //even bits
                    int j =0;
                    for (int i=0; i< numbits*2;i+=2) {
                            boolean isSet = false;
                            if ( i < buffer.length() )
                              isSet = buffer.charAt(i) == '1';
                            lonset.set(j++, isSet);
                    }
                   
                    //odd bits
                    j=0;
                    for (int i=1; i< numbits*2;i+=2) {
                            boolean isSet = false;
                            if ( i < buffer.length() )
                              isSet = buffer.charAt(i) == '1';
                            latset.set(j++, isSet);
                    }
                   //中国地理坐标:东经73°至东经135°,北纬4°至北纬53°
                    double lon = decode(lonset, 70, 140);
                    double lat = decode(latset, 0, 60);
                   
                    return new double[] {lat, lon};        
            }
           
            private double decode(BitSet bs, double floor, double ceiling) {
                    double mid = 0;
                    for (int i=0; i<bs.length(); i++) {
                            mid = (floor + ceiling) / 2;
                            if (bs.get(i))
                                    floor = mid;
                            else
                                    ceiling = mid;
                    }
                    return mid;
            }
           
           
            public String encode(double lat, double lon) {
                    BitSet latbits = getBits(lat, 0, 60);
                    BitSet lonbits = getBits(lon, 70, 140);
                    StringBuilder buffer = new StringBuilder();
                    for (int i = 0; i < numbits; i++) {
                            buffer.append( (lonbits.get(i))?'1':'0');
                            buffer.append( (latbits.get(i))?'1':'0');
                    }
                    return base32(Long.parseLong(buffer.toString(), 2));
            }
    
            private BitSet getBits(double lat, double floor, double ceiling) {
                    BitSet buffer = new BitSet(numbits);
                    for (int i = 0; i < numbits; i++) {
                            double mid = (floor + ceiling) / 2;
                            if (lat >= mid) {
                                    buffer.set(i);
                                    floor = mid;
                            } else {
                                    ceiling = mid;
                            }
                    }
                    return buffer;
            }
    
            public static String base32(long i) {
                    char[] buf = new char[65];
                    int charPos = 64;
                    boolean negative = (i < 0);
                    if (!negative)
                            i = -i;
                    while (i <= -32) {
                            buf[charPos--] = digits[(int) (-(i % 32))];
                            i /= 32;
                    }
                    buf[charPos] = digits[(int) (-i)];
    
                    if (negative)
                            buf[--charPos] = '-';
                    return new String(buf, charPos, (65 - charPos));
            }
    
    }
    


    球面距离公式:

    package com.DistTest;
    public class Test{ 
    	private static final  double EARTH_RADIUS = 6371000;//赤道半径(单位m)
    	
    	/**
    	 * 转化为弧度(rad)
    	 * */
    	private static double rad(double d)
    	{
    	   return d * Math.PI / 180.0;
    	}
    	/**
    	 * 基于googleMap中的算法得到两经纬度之间的距离,计算精度与谷歌地图的距离精度差点儿相同。相差范围在0.2米下面
    	 * @param lon1 第一点的精度
    	 * @param lat1 第一点的纬度
    	 * @param lon2 第二点的精度
    	 * @param lat3 第二点的纬度
    	 * @return 返回的距离,单位m
    	 * */
    	public static double GetDistance(double lon1,double lat1,double lon2, double lat2)
    	{
    	   double radLat1 = rad(lat1);
    	   double radLat2 = rad(lat2);
    	   double a = radLat1 - radLat2;
    	   double b = rad(lon1) - rad(lon2);
    	   double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2)+Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
    	   s = s * EARTH_RADIUS;
    	   s = Math.round(s * 10000) / 10000;
    	   return s;
    	}
    	 
    	public static void main(String []args){
    	      double lon1=109.0145193757;  
    	      double lat1=34.236080797698;
    	      double lon2=108.9644583556;
    	      double lat2=34.286439088548;
    	      double dist;
    	      String geocode;
    	      
    	      dist=Test.GetDistance(lon1, lat1, lon2, lat2); 
    	      System.out.println("两点相距:" + dist + " 米");
    	      
    	      
    	      Geohash geohash = new Geohash();
    	      geocode=geohash.encode(lat1, lon1);
    	      System.out.println("当前位置编码:" + geocode);
    	     
    	      geocode=geohash.encode(lat2, lon2);
    	      System.out.println("远方位置编码:" + geocode);
    
    	   }
    	//wqj7j37sfu03h2xb2q97
    	/*
    永相逢超市
    108.83457500177
    34.256981052624
    wqj6us6cmkj5bbfj6qdg
    s6q08ubhhuq7
    */
    }
    
    


    附近网点距离排序

    package com.DistTest;
      
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Connection;
    import java.sql.Statement;
     
     
    public class sqlTest {
    	
        public static void main(String[] args) throws Exception {
            Connection conn = null;
            String sql;
            String url = "jdbc:mysql://132.97.**.**/test?"
                    + "user=***&password=****&useUnicode=true&characterEncoding=UTF8";
     
            try {
                Class.forName("com.mysql.jdbc.Driver");// 动态载入mysql驱动
                // System.out.println("成功载入MySQL驱动程序");
                // 一个Connection代表一个数据库连接
                conn = DriverManager.getConnection(url);
                // Statement里面带有非常多方法,比方executeUpdate能够实现插入,更新和删除等
                Statement stmt = conn.createStatement();
                sql = "select * from retailersinfotable limit 1,10";
                ResultSet rs = stmt.executeQuery(sql);// executeQuery会返回结果的集合,否则返回空值
    	  	    double lon1=109.0145193757;  
    	  	    double lat1=34.236080797698;
                System.out.println("当前位置:");
                int i=0;
        		String[][] array = new String[10][3];
                while (rs.next()){
                		//从数据库取出地理坐标
                		double lon2=Double.parseDouble(rs.getString("Longitude"));
                		double lat2=Double.parseDouble(rs.getString("Latitude"));
                		
                		//依据地理坐标,生成geohash编码
    	          	    Geohash geohash = new Geohash();
    	        	    String geocode=geohash.encode(lat2, lon2).substring(0, 9);
    	        	    
    	        	    //计算两点间的距离
    	      	        int dist=(int) Test.GetDistance(lon1, lat1, lon2, lat2); 
    	      	        
    	      			array[i][0]=String.valueOf(i);
    	    			array[i][1]=geocode;
    	    			array[i][2]=Integer.toString(dist);
    	      			
    	      			i++;
            
                	//	System.out.println(lon2+"---"+lat2+"---"+geocode+"---"+dist);	
                    }
    
                array=sqlTest.getOrder(array); //二维数组排序
                sqlTest.showArray(array);        //打印数组
    
                
                
                
            } catch (SQLException e) {
                System.out.println("MySQL操作错误");
                e.printStackTrace();
            } finally {
                conn.close();
            }
     
        }
        /*
         * 二维数组排序,比較array[][2]的值。返回二维数组
         * */
        public static String[][] getOrder(String[][] array){
    		for (int j = 0; j < array.length ; j++) {
    			for (int bb = 0; bb < array.length - 1; bb++) {
    				String[] ss;
    				int a1=Integer.valueOf(array[bb][2]);  //转化成int型比較大小
    				int a2=Integer.valueOf(array[bb+1][2]);
    				if (a1>a2) {
    					ss = array[bb];
    					array[bb] = array[bb + 1];
    					array[bb + 1] = ss;
    					
    				}
    			}
    		} 
    		return array;
        }
        
        /*打印数组*/
        public static void showArray(String[][] array){
        	  for(int a=0;a<array.length;a++){
          		for(int j=0;j<array[0].length;j++)
          			System.out.print(array[a][j]+" ");
          		System.out.println();
          	}
        }
    
    }









  • 相关阅读:
    WEP编码格式
    OSK VFS read数据流分析
    科学剖析濒死体验 "复生"者讲述"死"前1秒钟
    Android的开发相对于tizen的开发难度
    minix文件系统分析
    贴给小程序(1) 查找第一个0值
    Linux下的QQ
    OSK USB 驱动
    LRU算法之hash+list实现(转)
    插入排序
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5139329.html
Copyright © 2011-2022 走看看