一 、 首先说下业务需求背景,因业务需求需要转换经纬度坐标,完全转换为utm 50N的格式。由于中国跨越了多个带区,在把utm 50N的格式的坐标转为wgs84经纬度的时候在西藏和新疆地区出现偏差,精确度下降。这个udf函数就是为了把全国的utm转换为wgs84经纬度的之后都是争取的或者说误差很小很小。

二、hive UDF的局限的地方是无法提前初始化开发这想要的对象,尤其是大的对象;如何是一个固定键值的文件的话还可以做map join,但是这个文件是没有键值的。在解决问题之前我我尝试看多种方式,看看源码是不是有我不知道的初始化方式,结果会是UDF真的太局限了,处理evaluate函数之外某用户别的东西了。也尝试把数据采用初始化数组的方式写在代码内,结果也不行,java根本不允许单个对象变量超过65536 个,更何况了我需要的是两百多万条数据。

       最后是想到了java的static {} 静态代码块的方式初始化一个静态数组,静态代码只会初始化一次。到这个也还有一个问题就是文件挡在哪里的问题,刚开始时放在jar内,分布式计算之后无法用相同的路径去完成;放在本地客户端上可以,但是需要进入hive shell的当前目录存在需要读取的文件,很多人用的情况下很麻烦。最后是采用了hadoop的api读取hdfs的方式。最终是 UDF + 静态代码+读取hdfs的方式解决了这个问题。执行过程中add jar的时候会去初始化g这个数组,需要几秒钟时间,这个是可以忍受的。

 

三、下面是完整代码

----------------------------------------------------------------------------

package cn.smartstep.extract.tables;
import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStreamReader;
 import java.io.Serializable;import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hive.ql.exec.UDF;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
 import cn.smartstep.extract.utm.Cast;@SuppressWarnings("all")
 public class UTMToLatLngFile extends UDF implements Serializable {    public static Cast[][] g = new Cast[1860][1420];
    public static void main(String[] args) {
    }
    /**
      * 静态代码只执行一次,可以通过Class.forName("classPath")的方式唤醒代码的static代码块。
      * 采用静态代码块初始化需要的查询数组,将数组的内容放在hdfs上,这种方式避免了从本地读取文件的麻烦。
      * 从本地读取文件是需要在每一个节点读要配置,而起进入hive shell的时候需要在当前目录存在数组配置文件。
      */
     static {
         
         try {            Configuration conf = new Configuration();// 读取配置文件
             // 流读入和写入
             InputStream in = null;
             // 获取HDFS的conf
             // 读取HDFS上的文件系统
             FileSystem hdfs = FileSystem.get(conf);
             // 使用缓冲流,进行按行读取的功能
             BufferedReader buff = null;
             // 获取日志文件的根目录
             Path listf = new Path("hdfs:///user/ss_deploy/jars/grid_whome");
             // 获取根目录下的所有2级子文件目录
             FileStatus stats[] = hdfs.listStatus(listf);
             // 自定义j,方便查看插入信息
             //int j = 0;
             for (int i = 0; i < stats.length; i++) {
                 // 获取子目录下的文件路径
                 FileStatus temp[] = hdfs.listStatus(new Path(stats[i].getPath().toString()));
                 for (int k = 0; k < temp.length; k++) {
                     //System.out.println("文件路径名:" + temp[k].getPath().toString());
                     // 获取Path
                     Path p = new Path(temp[k].getPath().toString());
                     // 打开文件流
                     in = hdfs.open(p);
                     // BufferedReader包装一个流
                     buff = new BufferedReader(new InputStreamReader(in));
                     String str = null;
                     while ((str = buff.readLine()) != null) {
                         String[] o = str.split("\\|");
                         int ii = Integer.parseInt(o[0]);
                         int jj = Integer.parseInt(o[1]);
                         Double x = Double.parseDouble(o[2]);
                         Double y = Double.parseDouble(o[3]);
                         Double lat = Double.parseDouble(o[4]);
                         Double lng = Double.parseDouble(o[5]);
                         g[ii][jj] = new Cast(x, y, lat, lng);                    }
                     buff.close();
                     in.close();
                 }
             }
             hdfs.close();            /* 读入TXT文件 */
             // String pathname = "src/main/resources/test.txt"; //
             // 绝对路径或相对路径都可以,这里是绝对路径,写入文件时演示相对路径
             // String pathname =
             // "src/main/resources/fixssutm-1860x1420-00000.txt";            /*
              * String pathname = "fixssutm-1860x1420-00000.txt"; File filename =
              * new File(pathname); // 要读取以上路径的input。txt文件 InputStreamReader
              * reader = new InputStreamReader( new FileInputStream(filename));
              * // 建立一个输入流对象reader BufferedReader br = new
              * BufferedReader(reader); // 建立一个对象,它把文件内容转成计算机能读懂的语言 String line =
              * ""; line = br.readLine(); while (line != null) { line =
              * br.readLine(); // 一次读入一行数据 if (line != null) { String[] o =
              * line.split("\\|"); int i = Integer.parseInt(o[0]); int j =
              * Integer.parseInt(o[1]); Double x = Double.parseDouble(o[2]);
              * Double y = Double.parseDouble(o[3]); Double lat =
              * Double.parseDouble(o[4]); Double lng = Double.parseDouble(o[5]);
              *
              * g[i][j] = new Cast(x, y, lat, lng); } }
              */        } catch (Exception e) {
             e.printStackTrace();
         }    }
    /**
     *
     *@Description :hive udf函数的入口
     * @param X (utm的x)
     * @param Y (utm的y)
     * @throws Exception
     * @time : 2019年12月30日下午2:52:39
     * @author :wangqj
     * @return :String
     */
     public String evaluate(int X, int Y) throws Exception {
         Double[] latlng = gux1000((double) X, (double) Y);
         return latlng[0] + "," + latlng[1];    }
    /**
      *@Description :divmod函数读取x和y的模和余数
      * @param x
      * @param y
      * @time : 2019年12月30日下午2:55:19
      * @author :wangqj
      * @return :int[]
      */
     public int[] divmod(int x, int y) {
         int[] amod = new int[2];
         amod[0] = x / y;
         amod[1] = x % y;
         return amod;
     }    /**
      *@Description :读取差值数组,查询经纬度
      * @param x2
      * @param y2
      * @param grid
      * @param x0
      * @param y0
      * @param xstep
      * @param ystep
      * @time : 2019年12月30日下午2:56:14
      * @author :wangqj
      * @return :Double[]
      */
     public Double[] gux(Double x2, Double y2, Cast[][] grid, int x0, int y0,
             int xstep, int ystep) {
         Double[] latlng = new Double[2];        Double xx = (x2 - x0);
         int[] irx = divmod(xx.intValue(), xstep);
         int i = irx[0];
         Double rx = (double) irx[1];
         Double yy = (y2 - y0);
         int[] jry = divmod(yy.intValue(), ystep);
         int j = jry[0];
         Double ry = (double) jry[1];        Cast gridij = grid[i][j];
         Cast gridi1j = grid[i + 1][j];
         Cast gridij1 = grid[i][j + 1];        Double lat = gridij.tlat + rx / xstep * (gridi1j.tlat - gridij.tlat)
                 + ry / ystep * (gridij1.tlat - gridij.tlat);
         Double lng = gridij.tlon + rx / xstep * (gridi1j.tlon - gridij.tlon)
                 + ry / ystep * (gridij1.tlon - gridij.tlon);        latlng[0] = lat;
         latlng[1] = lng;
         return latlng;
     }     /**
      *
      * @Description :读取
      * @param x2
      * @param y2
      * @return
      * @time : 2019年12月30日下午2:56:36
      * @author :wangqj
      * @return :Double[]
      */
     public Double[] gux1000(Double x2, Double y2) {
         //utm坐标x的值域(-6800000, 1600000)
         return gux(x2, y2, g, -6800000, 1600000, 5000, 5000);
     }}
 
package cn.smartstep.extract.utm;
import java.io.Serializable;
@SuppressWarnings("all")
 public class Cast implements Serializable {    public Double tx;
     public Double ty;
     public Double tlat;
     public Double tlon;
     
     public Cast(Double tx, Double ty, Double tlat, Double tlon) {
         super();
         this.tx = tx;
         this.ty = ty;
         this.tlat = tlat;
         this.tlon = tlon;
     }
     public Double getTx() {
         return tx;
     }
     public void setTx(Double tx) {
         this.tx = tx;
     }
     public Double getTy() {
         return ty;
     }
     public void setTy(Double ty) {
         this.ty = ty;
     }
     public Double getTlat() {
         return tlat;
     }
     public void setTlat(Double tlat) {
         this.tlat = tlat;
     }
     public Double getTlon() {
         return tlon;
     }
     public void setTlon(Double tlon) {
         this.tlon = tlon;
     }
     
 }

 

--------------------------------------------------------------------------------------------------------------------------------------------

需要读取的文件格式:

17|253|-6682870.711377543|2911374.176077393|15.017040630294595|60.07745523400567
 17|254|-6682471.324196002|2916379.345332657|15.042378926184604|60.0692926510859
 17|255|-6682071.076545545|2921383.9669462577|15.06771097398942|60.06111348512968
 17|256|-6681670.070101743|2926388.3746052873|15.093035364394279|60.052925247075486
 17|257|-6681268.566914886|2931392.427066926|15.118358336705251|60.04472201675457
 17|258|-6680866.221413381|2936395.796781277|15.143676772048403|60.03649840491263
 17|259|-6680463.086116834|2941398.774157732|15.168989283657806|60.02826018117868
 17|260|-6680059.309639597|2946401.340439431|15.194298266057492|60.02000675986658
 17|261|-6679654.907294498|2951403.4194463813|15.21960425900364|60.01173711365137
 17|262|-6679249.6075822795|2956405.0461663115|15.244902656073505|60.00345279469018