hive 的UDF和java的静态代码块的完美结果

一 、 首先说下业务需求背景,因业务需求需要转换经纬度坐标,完全转换为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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值