压缩算法java(java压缩算法策略)

  • 时间:
  • 7993人关注

这是一篇关于java压缩算法策略的编程热门专题内容,被220位程序员关注,内容涉及到压缩算法、java、java字符串压缩算法等,由高子怀编辑补充。

码农之家
精选文章1

9小时37分钟前整理

Java编程实现轨迹压缩算法开放窗口实例代码

轨迹压缩算法

场景描述

给定一个GPS数据记录文件,每条记录包含经度和维度两个坐标字段,根据距离阈值压缩记录,将过滤后的所有记录的经纬度坐标构成一条轨迹

算法描述

这种算法的用处还是相当广泛的。

轨迹压缩算法分为两大类,分别是无损压缩和有损压缩,无损压缩算法主要包括哈夫曼编码,有损压缩算法又分为批处理方式和在线数据压缩方式,其中批处理方式又包括DP(Douglas-Peucker)算法、TD-TR(Top-Down Time-Ratio)算法和Bellman算法,在线数据压缩方式又包括滑动窗口、开放窗口、基于安全区域的方法等。

大家也可参考这篇文章:Java编程实现轨迹压缩之Douglas-Peucker算法详细代码

代码实现

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TrajectoryCom {
	public static void main(String[] args) throws Exception{
		//阈值定义
		double maxDistanceError = 30;
		/*
  * 文件读取
  * */
		//存放从文件读取的位置点的信息列表
		ArrayList<enpoint> ENPList = new ArrayList<enpoint>();
		//源数据文件的地址 建立文件对象
		//这里是需要更改的地方 改你源文件的存放地址 记住如果地址中含"\",记得再加一个"\",原因"\"是转义符号 
		//这里可以写成C:/Users/Administrator/Desktop/11.6/2007-10-14-GPS.log
		File sourceFile = new File("./2007-10-14-GPS.log");
		//调用文件读取函数 读取文件数据
		ENPList = getENPointFromFile(sourceFile);
		//这里是测试 有没有读到里面 看看列表里的数据个数 交作业的时候记得注释掉
		System.out.println(ENPList.size());
		/*
  * 数据处理
  * 方法:开放窗口轨迹压缩法
  * */
		//存放目标点的集合
		ArrayList<enpoint> rePointList = new ArrayList<enpoint>();
		rePointList = openWindowTra(ENPList,maxDistanceError);
		System.out.println(rePointList.size());
		/*
  * 写入目标文件
  * */
		File targetFile = new File("./2007-10-14-GPSResult.log");
		writeTestPointToFile(targetFile,rePointList);
		/*
  * 压缩率计算
  */
		double cpL = (double)rePointList.size() / (double)ENPList.size() * 100;
		DecimalFormat df = new DecimalFormat("0.000000");
		System.out.println("压缩率:"+ df.format(cpL) + "%");
		/*
  * 计算平均距离误差
  * */
		double aveDisErr = getMeanDistError(ENPList,rePointList);
		System.out.println(aveDisErr);
		/*
  * 画线形成对比图
  * */
		//generateImage(ENPList,rePointList);
	}
	/*
 * 从提供的文件信息里提取位置点
 * 并将每个点的坐标数值调用转换函数存到列表里
 * 函数返回一个 存放所有位置点 的集合
 */
	public static ArrayList<enpoint> getENPointFromFile(File fGPS)throws Exception{
		ArrayList<enpoint> pGPSArray = new ArrayList<enpoint>();
		if(fGPS.exists()&&fGPS.isFile()){
			InputStreamReader read = new InputStreamReader(new FileInputStream(fGPS));
			//输入流初始化
			BufferedReader bReader = new BufferedReader(read);
			//缓存读取初始化
			String str;
			String[] strGPS;
			int i = 0;
			while((str = bReader.readLine())!=null){
				//每次读一行
				strGPS = str.split(" ");
				ENPoint p = new ENPoint();
				p.id = i;
				i++;
				p.pe = (dfTodu(strGPS[3]));
				p.pn = (dfTodu(strGPS[5]));
				pGPSArray.add(p);
			}
			bReader.close();
		}
		return pGPSArray;
	}
	/**
 * 函数功能:将原始经纬度坐标数据转换成度
 * 获取的经纬度数据为一个字符串
 */
	public static double dfTodu(String str){
		int indexD = str.indexOf('.');
		//获取 . 字符所在的位置
		String strM = str.substring(0,indexD-2);
		//整数部分
		String strN = str.substring(indexD-2);
		//小数部分
		double d = double.parsedouble(strM)+double.parsedouble(strN)/60;
		return d;
	}
	/*
 * 开放窗口方法实现
 * 返回一个压缩后的位置列表
 * 列表每条数据存放ID、点的坐标
 * 
 * 算法描述:
 * 初始点和浮动点计算出投影点,判断投影点和轨迹点的距离与阈值 若存在距离大于阈值 
 * 则初始点放入targetList,浮动点向前检索一点作为新的初始点,新的初始点向后检索第二个作为新的浮动点 这里存在判断 即新的初始点位置+1是不是等于列表长度 这里决定了浮动点的选取
 * 如此处理至终点
 * */
	public static ArrayList<enpoint> openWindowTra(ArrayList<enpoint> sourceList,double maxDis){
		ArrayList<enpoint> targetList = new ArrayList<enpoint>();
		//定义初始点位置 最开始初始点位置为0 
		int startPoint = 0;
		//定义浮动点位置 最开始初始点位置2
		int floatPoint = 2;
		//定义当前轨迹点位置 最开始初始点位置为1
		int nowPoint = 1;
		int len = sourceList.size();
		//存放所有窗口内的点的信息集合 
		ArrayList<enpoint> listPoint = new ArrayList<enpoint>();
		listPoint.add(sourceList.get(nowPoint));
		//浮动点位置决定循环
		while(true){
			//标志 用来控制判断是否进行窗口内轨迹点更新
			Boolean flag = false;
			//计算并判断窗口内所有点和投影点的距离是否大于阈值
			for (ENPoint point:listPoint){
				double disOfTwo = getDistance(sourceList.get(startPoint),sourceList.get(floatPoint),point);
				if(disOfTwo >= 30){
					flag = true;
					break;
				}
			}
			if(flag){
				//窗口内点距离都大于阈值
				//初始点加到目标列表
				targetList.add(sourceList.get(startPoint));
				//初始点变化
				startPoint = floatPoint - 1;
				//浮动点变化
				floatPoint += 1;
				if(floatPoint >= len){
					targetList.add(sourceList.get(floatPoint-1));
					break;
				}
				//窗口内点变化
				listPoint.clear();
				//System.out.println(listPoint.size());
				listPoint.add(sourceList.get(startPoint+1));
			} else{
				//距离小于阈值的情况
				//初始点不变
				//当前窗口集合加入当前浮动点
				listPoint.add(sourceList.get(floatPoint));
				//浮动点后移一位
				floatPoint += 1;
				//如果浮动点是终点 且当前窗口点距离都小于阈值 就直接忽略窗口点 直接将终点加入目标点集合
				if(floatPoint >= len){
					targetList.add(sourceList.get(startPoint));
					targetList.add(sourceList.get(floatPoint-1));
					break;
				}
			}
			flag = false;
		}
		return targetList;
	}
	/*计算投影点到轨迹点的距离
 * 入口是初始点A、浮动点B、当前轨迹点C
 * 三角形面积公式
 */
	public static double getDistance(ENPoint A,ENPoint B,ENPoint C){
		double distance = 0;
		double a = Math.abs(geoDist(A,B));
		double b = Math.abs(geoDist(B,C));
		double c = Math.abs(geoDist(A,C));
		double p = (a + b + c)/2.0;
		double s = Math.sqrt(p * (p-a) * (p-b) * (p-c));
		distance = s * 2.0 / a;
		return distance;
	}
	/*
 * ArrayList 拷贝函数
 * */
	/*提供的函数
 * 其中计算距离的函数 经过改造得到下面的距离计算方法
 * 具体是怎么计算距离的 我也没研究了
 * */
	public static double geoDist(ENPoint pA,ENPoint pB){
		double radLat1 = Rad(pA.pn);
		double radLat2 = Rad(pB.pn);
		double delta_lon = Rad(pB.pe - pA.pe);
		double top_1 = Math.cos(radLat2) * Math.sin(delta_lon);
		double top_2 = Math.cos(radLat1) * Math.sin(radLat2) - Math.sin(radLat1) * Math.cos(radLat2) * Math.cos(delta_lon);
		double top = Math.sqrt(top_1 * top_1 + top_2 * top_2);
		double bottom = Math.sin(radLat1) * Math.sin(radLat2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.cos(delta_lon);
		double delta_sigma = Math.atan2(top, bottom);
		double distance = delta_sigma * 6378137.0;
		return distance;
	}
	public static double Rad(double d){
		return d * Math.PI / 180.0;
	}
	/*
 * 将压缩后的位置点信息写入到文件中
 * */
	public static void writeTestPointToFile(File outGPSFile,ArrayList<enpoint> pGPSPointFilter)throws Exception{
		Iterator<enpoint> iFilter = pGPSPointFilter.iterator();
		RandomAccessFile rFilter = new RandomAccessFile(outGPSFile,"rw");
		while(iFilter.hasNext()){
			ENPoint p = iFilter.next();
			String sFilter = p.getResultString();
			byte[] bFilter = sFilter.getBytes();
			rFilter.write(bFilter);
		}
		rFilter.close();
	}
	/**
 * 函数功能:求平均距离误差
 * 返回平均距离
 */
	public static double getMeanDistError(ArrayList<enpoint> pGPSArray,ArrayList<enpoint> pGPSArrayRe){
		double sumDist = 0.0;
		for (int i=1;i<pgpsarrayre.size();i++){
			double="" end="pGPSArrayRe.get(i).id;" int="" j="start+1;j<end;j++){" meandist="sumDist/(pGPSArray.size());" pre="" return="" start="pGPSArrayRe.get(i-1).id;" sumdist=""><pre class="brush:java;">import java.text.DecimalFormat;
			public class ENPoint implements Comparable<enpoint>{
			 public int id;
			//点ID
			public double pe;
			//经度
			public double pn;
			//维度
			public ENPoint(){
			}
			//空构造函数
			public String toString(){
				return this.id+"#"+this.pn+","+this.pe;
			}
			public String getResultString(){
				DecimalFormat df = new DecimalFormat("0.000000");
				return this.id+"#"+df.format(this.pe)+","+df.format(this.pn)+" \n";
			}
			@Override
			 public int compareTo(ENPoint other) {
				if(this.id<other.id) else="" return="" this.id="">other.id) return 1; else
				  return 0;
			}
		}

总结

以上就是本文关于Java编程实现轨迹压缩算法开放窗口实例代码的全部内容,希望对大家有所帮助。如有不足之处,欢迎留言指出。

展开阅读
码农之家
精选文章2

6小时10分钟前整理

Java压缩之LZW算法字典压缩与解压讲解

压缩过程:

前面已经写过一篇哈夫曼压缩,LZW字典压缩与哈夫曼压缩的不同之处在于不需要把编码写入文件,编码表是在读文件中生成的,首先将0-255个ASCLL码与对应的数字存入哈希表中,作为基础码表。

Java压缩之LZW算法字典压缩与解压讲解

这里的后缀为当前

前缀+后缀 如果在码表中存在,前缀等于前缀+后缀。如果不存在,将前缀+后缀所表示的字符串写入编码表编码,同时将前缀写入压缩文件中。这里重点注意一下,一个字节所能表示的数字范围为0-255,所以我们将一个字符的编码变成两个字节写进去,分别写入它的高八位和低八位,比如256即为00000001 11111111 这里用到DataOutputStream dos对象中的 dos.writeChar(256)方法。

两个字节所能表示的范围为0-65535。当我们的编码超过这份范围,就需要重置编码表,再重新编码。

解压过程

Java压缩之LZW算法字典压缩与解压讲解

CW表示读取的到的字符,PW为上一行的CW,CW再编码表中存在:P→PW,C→CW的第一个字符,输出CW。CW在编码表中不存在,P→PW,C→PW的第一字符输出P+C。

当我们读到65535的时候,就重置码表,重新编码。

代码部分

public class Yasuo {
 private int bianma = 256;// 编码
 private String perfix = "";// 前缀
 private String suffix = "";// 后缀
 private String zhongjian = "";// 中间变量
 HashMap<String, Integer> hm = new HashMap<String, Integer>();// 编码表
 private static String path = "D:\\JAVA\\字典压缩\\zidianyasuo.txt";// 要压缩的文件
 private static String path2 = "D:\\JAVA\\字典压缩\\yasuo.txt";// 解压后的文件
 private static String path3 = "D:\\JAVA\\字典压缩\\jieya.txt";// 解压后的文件
 public static void main(String[] args) throws IOException {
 /**
  * 压缩
  */
 Yasuo yasuo = new Yasuo();
 yasuo.yasuo();
 /**
  * 解压
  */
 Jieya jie = new Jieya();
 jie.jieya(path2,path3);
 }
 public void yasuo() throws IOException {
 // 创建文件输入流
 InputStream is = new FileInputStream(path);
 byte[] buffer = new byte[is.available()];// 创建缓存区域
 is.read(buffer);// 读入所有的文件字节
 String str = new String(buffer);// 对字节进行处理
 is.close(); // 关闭流
 // 创建文件输出流
 OutputStream os = new FileOutputStream(path2);
 DataOutputStream dos = new DataOutputStream(os);
// System.out.println(str);
 // 把最基本的256个Ascll码放编码表中
 for (int i = 0; i < 256; i++) {
  char ch = (char) i;
  String st = ch + "";
  hm.put(st, i);
 }
 for (int i = 0; i < str.length(); i++) {
  if(bianma==65535){
  System.out.println("重置");
  dos.writeChar(65535);//写出一个-1作为重置的表示与码表的打印
  hm.clear();//清空Hashmap
  for (int j = 0; j < 256; j++) {//重新将基本256个编码写入
   char ch = (char) j;
   String st = ch + "";
   hm.put(st, j);
  }
  perfix="";
  bianma=0;
  }
  char ch = str.charAt(i);
  String s = ch + "";
  suffix = s;
  zhongjian = perfix + suffix;
  if (hm.get(zhongjian) == null) {// 如果码表中没有 前缀加后缀的码表
//  System.out.print(zhongjian);
//  System.out.println(" 对应的编码为 " + bianma);
  hm.put(zhongjian, bianma);// 向码表添加 前缀加后缀 和 对应的编码
//  System.out.println(" " + perfix);
//  System.out.println("写入的编码 "+hm.get(perfix));
  dos.writeChar(hm.get(perfix)); // 把前缀写入压缩文件
  bianma++;
  perfix = suffix;
  } else {// 如果有下一个前缀保存 上一个前缀加后缀
  perfix = zhongjian;
  }
  if (i == str.length() - 1) {// 把最后一个写进去
//  System.out.print("写入最后一个"+perfix);
  dos.writeChar(hm.get(perfix));
//  System.out.println("   "+hm.get(perfix));
  }
 }
 os.close();// 关闭流
// System.out.println(hm.toString());// 输出码表
 }
}
public class Jieya {
 private ArrayList<Integer> list = new ArrayList<Integer>();// 存高八位
 private int count = 0;// 下标
 private ArrayList<Integer> numlist = new ArrayList<>();// 存编码
 HashMap<String, Integer> hm = new HashMap<>();// 编码表
 HashMap<Integer, String> hm1 = new HashMap<>();// 编码表
 private String cw = "";
 private String pw = "";
 private String p = "";
 private String c = "";
 private int bianma = 256;
 public void jieya(String path, String path1) throws IOException {
 // 读取压缩文件
 InputStream is = new FileInputStream(path);
 byte[] buffer = new byte[is.available()];
 is.read(buffer);
 is.close();// 关闭流
 String str = new String(buffer);
 // System.out.println(str);
 // 读高八位 把高八位所表示的数字放入List中
 for (int i = 0; i < buffer.length; i += 2) {
  int a = buffer[i];
  list.add(a);// 高八位存入list列表中
 }
 for (int i = 1; i < buffer.length; i += 2) {// 读低八位
  // System.out.println(list.get(count)+"---");
  if (buffer[i] == -1 && buffer[i - 1] == -1) {
  numlist.add(65535);
  } else {
  // System.out.println(i);
  if (list.get(count) > 0) {// 如果低八位对应的高八位为1
   if (buffer[i] < 0) {
   int a = buffer[i] + 256 + 256 * list.get(count);
   // buffer[i]+=256+256*list.get(count);
   numlist.add(a);// 存入numlist中
   } else {
   int a = buffer[i] + 256 * (list.get(count));
   // System.out.println(buffer[i]+" "+a + "+++");
   numlist.add(a);// 存入numlist中
   }
  } else {// 高八位为0
   // System.out.println(buffer[i]);
   numlist.add((int) buffer[i]);// 存入numlist中
  }
  count++;
  }
 }
 // System.out.println(list.size()+" "+count+" "+numlist.size()+"比较大小"+"
 // "+buffer.length);
 // for(int i=0;i<numlist.size();i++){
 // System.out.println(numlist.get(i)+"p");
 // }
 /**
  * 把0-255位字符编码
  */
 for (int i = 0; i < 256; i++) {
  char ch = (char) i;
  String st = ch + "";
  hm.put(st, i);
  hm1.put(i, st);
 }
 /**
  * 根据numlist队列中的元素开始重新编码,输出文件
  */
 // 创建输出流
 OutputStream os = new FileOutputStream(path1);
 // 遍历numlist
 for (int i = 0; i < numlist.size(); i++) {
  int n = numlist.get(i);
  if (hm.containsValue(n) == true) {// 如果编码表中存在
  cw = hm1.get(n);
  // System.out.println(cw+"*");
  if (pw != "") {
   os.write(cw.getBytes("gbk"));
   p = pw;
   c = cw.charAt(0) + "";// c=cw的第一个
   // System.out.println(c+"&");
   hm.put(p + c, bianma);
   hm1.put(bianma, p + c);
   bianma++;
  } else {
   os.write(cw.getBytes("gbk"));// 第一个
  }
  } else {// 编码表中不存在
  p = pw;
  // System.out.println(pw+"-=");
  c = pw.charAt(0) + "";// c=pw的第一个
  hm.put(p + c, bianma);
  hm1.put(bianma, p + c);
  bianma++;
  os.write((p + c).getBytes("gbk"));
  cw = p + c;
  }
  pw = cw;
  // System.out.println(bianma);
  // System.out.println(cw+"==");
  if (i == 65535) {
  System.out.println("重置2");
  hm.clear();
  hm1.clear();
  for (int j = 0; j < 256; j++) {
   char ch = (char) j;
   String st = ch + "";
   hm.put(st, j);
   hm1.put(j, st);
  }
  bianma = 0;
  pw = "";
  }
 }
 // System.out.println(hm1.toString());
 os.close();
 }
}

不足之处:当编码超过65535的时候,并没有处理好,不能重置码表,还原出的文件在超过65535的部分就开始乱码。还有待改善。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对码农之家的支持。如果你想了解更多相关内容请查看下面相关链接

展开阅读
码农之家
精选文章3

1小时37分钟前整理

Java编程实现轨迹压缩之Douglas-Peucker算法详细代码

第一部分 问题描述

1.1 具体任务

  本次作业任务是轨迹压缩,给定一个GPS数据记录文件,每条记录包含经度和维度两个坐标字段,所有记录的经纬度坐标构成一条轨迹,要求采用合适的压缩算法,使得压缩后轨迹的距离误差小于30m。

1.2 程序输入

  本程序输入是一个GPS数据记录文件。

1.3 数据输出

  输出形式是文件,包括三部分,压缩后点的ID序列及坐标、点的个数、平均距离误差、压缩率

第二部分 问题解答

  根据问题描述,我们对问题进行求解,问题求解分为以下几步:

2.1 数据预处理

  本次程序输入为GPS数据记录文件,共有3150行记录,每行记录又分为若干个字段,根据题意,我们只需关注经度和纬度坐标字段即可,原始数据文件部分记录如图2.1所示:

Java编程实现轨迹压缩之Douglas-Peucker算法详细代码

图2.1 原始数据文件部分记录示意图

 如图2.1所示,原始数据文件每条记录中经纬度坐标字段数据的保存格式是典型的GPS坐标表达方式,即度分格式,形式为dddmm.mmmm,其中ddd表示度,mm.mmmm表示分,小数点前面表示分的整数部分,小数点后表示分的小数部分;本次数据预处理,为方便后面两个坐标点之间距离的计算,我们需要将度分格式的经纬度坐标数据换算成度的形式,换算方法是ddd+mm.mmmm/60,此处我们保留小数点后6位数字,换算后的形式为ddd.xxxxxx。

  我们以第一条记录中经纬度坐标(11628.2491,3955.6535)为例,换算后的结果为(116.470818,39.927558),所有记录中经纬度坐标都使用方法进行,并且可以为每一个转换后的坐标点生成一个ID,进行唯一标识,压缩后,我们只需输出所有被保留点的ID即可。

2.2 Douglas-Peucker轨迹压缩算法

  轨迹压缩算法分为两大类,分别是无损压缩和有损压缩,无损压缩算法主要包括哈夫曼编码,有损压缩算法又分为批处理方式和在线数据压缩方式,其中批处理方式又包括DP(Douglas-Peucker)算法、TD-TR(Top-Down Time-Ratio)算法和Bellman算法,在线数据压缩方式又包括滑动窗口、开放窗口、基于安全区域的方法等。

  由于时间有限,本次轨迹压缩,我们决定采用相对简单的DP算法。

  DP算法步骤如下:

  (1)在轨迹曲线在曲线首尾两点A,B之间连接一条直线AB,该直线为曲线的弦;

  (2)遍历曲线上其他所有点,求每个点到直线AB的距离,找到最大距离的点C,最大距离记为dmax;

  (3)比较该距离dmax与预先定义的阈值Dmax大小,如果dmax<Dmax,则将该直线AB作为曲线段的近似,曲线段处理完毕;

  (4)若dmax>=Dmax,则使C点将曲线AB分为AC和CB两段,并分别对这两段进行(1)~(3)步处理;

  (5)当所有曲线都处理完毕时,依次连接各个分割点形成的折线,即为原始曲线的路径。

2.3 点到直线的距离

  DP算法中需要求点到直线的距离,该距离指的是垂直欧式距离,即直线AB外的点C到直线AB的距离d,此处A、B、C三点均为经纬度坐标;我们采用三角形面积相等法求距离d,具体方法是:A、B、C三点构成三角形,该三角形的面积有两种求法,分别是普通方法(底x高/2)和海伦公式,海伦公式如下:

  假设有一个三角形,边长分别为a、b、c,三角形的面积S可由以下公式求得:

Java编程实现轨迹压缩之Douglas-Peucker算法详细代码

其中p为半周长:

Java编程实现轨迹压缩之Douglas-Peucker算法详细代码

我们通过海伦公式求得三角形面积,然后就可以求得高的大小,此处高即为距离d。要想用海伦公式,必须求出A、B、C三点两两之间的距离,该距离公式是由老师给出的,直接调用距离函数即可。

注意:求出距离后,要加上绝对值,以防止距离为负数。

2.4 平均误差求解

  平均误差指的是压缩时忽略的那些点到对应线段的距离之和除以总点数得到的数值。

2.5 压缩率求解

  压缩率的计算公式如下:

Java编程实现轨迹压缩之Douglas-Peucker算法详细代码

2.6 数据结果文件的生成

  经过上面的处理和计算,我们将压缩后剩余点的ID和点的个数、平均距离误差、压缩率等参数都写入最终的结果文件中,问题解答完成。

第三部分 代码实现

  本程序采用Java语言编写,开发环境为IntelliJ IDEA 14.0.2,代码共分为两个类,一个是ENPoint类,用于保存经纬度点信息,一个是TrajectoryCompressionMain类,用于编写数据处理、DP算法、点到直线距离、求平均误差等函数。

3.1 程序总流程

  整个程序流程主要包括以下几个步骤:

  (1)定义相关ArrayList数组和File对象,其中ArrayList数组对象有三个,分别是原始经纬度坐标数组pGPSArryInit、过滤后的点坐标数组pGPSArrayFilter、过滤并排序后的点坐标数组pGPSArrayFilterSort;File文件对象共有五个,分别是原始数据文件对象fGPS、压缩后的结果数据文件对象oGPS、保持转换后的原始经纬度坐标点的数据文件fInitGPSPoint、仿真测试文件fTestInitPoint和fTestFilterPoint。

  (2)获取原始点坐标并将其写入到文件中,主要包括读文件和写文件两种操作;

  (3)进行轨迹压缩;

  (4)对压缩后的经纬度点坐标进行排序;

  (5)生成仿真测试文件,并用R语言工具进行图形绘制,得到最终的结果;

  (6)求平均误差和压缩率,平均误差通过函数求得,压缩率直接计算获得;

  (7)将最终结果写入结果文件中,包括过滤后的点的ID,点的个数、平均误差和压缩率;

3.2 具体实现代码

  (1)ENPoint.java

package cc.xidian.main;
import java.text.DecimalFormat;
/**
* Created by hadoop on 2015/12/20.
*/
public class ENPoint implements Comparable<ENPoint>{
public int id;
//点ID
public double pe;
//经度
public double pn;
//维度
public ENPoint(){
}
//空构造函数
public String toString(){
	//DecimalFormat df = new DecimalFormat("0.000000");
	return this.id+"#"+this.pn+","+this.pe;
}
public String getTestString(){
	DecimalFormat df = new DecimalFormat("0.000000");
	return df.format(this.pn)+","+df.format(this.pe);
}
public String getResultString(){
	DecimalFormat df = new DecimalFormat("0.000000");
	return this.id+"#"+df.format(this.pn)+","+df.format(this.pe);
}
@Override
public int compareTo(ENPoint other) {
	if(this.id<other.id) return -1; else if(this.id>other.id) return1; else
	return0;
}
}

(2)TrajectoryCompressionMain.java

package cc.xidian.main;
import java.io.*;
import java.text.DecimalFormat;
import java.util.*;
import java.util.List;
/**
* Created by hadoop on 2015/12/19.
*/
public class TrajectoryCompressionMain{
	public static void main(String[] args)throws Exception{
		//-----------------------1、相关ArrayList数组和File对象的声明和定义-------------------------------------------------//
		ArrayList<ENPoint> pGPSArrayInit = new ArrayList<ENPoint>();
		//原纪录经纬度坐标数组
		ArrayList<ENPoint> pGPSArrayFilter = new ArrayList<ENPoint>();
		//过滤后的经纬度坐标数组
		ArrayList<ENPoint> pGPSArrayFilterSort = new ArrayList<ENPoint>();
		//过滤并排序后的经纬度坐标数组
		File fGPS = new File("2007-10-14-GPS.log");
		//原始数据文件对象
		File oGPS = new File("2015-12-25-GPS-Result.log");
		//过滤后的结果数据文件对象
		//保持转换成度后的原始经纬度数据文件,保持格式为“ID#经纬值,纬度值”,其中经度和维度单位为度,并保留小数点后6位数字
		File fInitGPSPoint = new File("2007-10-14-GPS-ENPoint.log");
		//保持转换后的原始经纬度坐标点的数据文件
		File fTestInitPoint = new File("2007-10-14-GPS-InitTestPoint.log");
		//用于仿真的原始经纬度坐标点数据文件
		File fTestFilterPoint = new File("2015-12-25-GPS-FilterTestPoint.log");
		//用于仿真的过滤后的经纬度坐标点数据文件
		//-------------------------2、获取原始点坐标并将其写入到文件中-------------------------------------------------------//
		pGPSArrayInit = getENPointFromFile(fGPS);
		//从原始数据文件中获取转换后的经纬度坐标点数据,存放到ArrayList数组中
		writeInitPointToFile(fInitGPSPoint, pGPSArrayInit);
		//将转换后的原始经纬度点数据写入文件中
		System.out.println(pGPSArrayInit.size());
		//输出原始经纬度点坐标的个数
		//-------------------------3、进行轨迹压缩-----------------------------------------------------------------------//
		double DMax = 30.0;
		//设定最大距离误差阈值
		pGPSArrayFilter.add(pGPSArrayInit.get(0));
		//获取第一个原始经纬度点坐标并添加到过滤后的数组中
		pGPSArrayFilter.add(pGPSArrayInit.get(pGPSArrayInit.size()-1));
		//获取最后一个原始经纬度点坐标并添加到过滤后的数组中
		ENPoint[] enpInit = new ENPoint[pGPSArrayInit.size()];
		//使用一个点数组接收所有的点坐标,用于后面的压缩
		Iterator<ENPoint> iInit = pGPSArrayInit.iterator();
		int jj=0;
		while(iInit.hasNext()){
			enpInit[jj] = iInit.next();
			jj++;
		}
		//将ArrayList中的点坐标拷贝到点数组中
		int start = 0;
		//起始下标
		int end = pGPSArrayInit.size()-1;
		//结束下标
		TrajCompressC(enpInit,pGPSArrayFilter,start,end,DMax);
		//DP压缩算法
		System.out.println(pGPSArrayFilter.size());
		//输出压缩后的点数
		//-------------------------4、对压缩后的经纬度点坐标数据按照ID从小到大排序---------------------------------------------//
		ENPoint[] enpFilter = new ENPoint[pGPSArrayFilter.size()];
		//使用一个点数组接收过滤后的点坐标,用于后面的排序
		Iterator<ENPoint> iF = pGPSArrayFilter.iterator();
		int i = 0;
		while(iF.hasNext()){
			enpFilter[i] = iF.next();
			i++;
		}
		//将ArrayList中的点坐标拷贝到点数组中
		Arrays.sort(enpFilter);
		//进行排序
		for (int j=0;j<enpFilter.length;j++){
			pGPSArrayFilterSort.add(enpFilter[j]);
			//将排序后的点坐标写到一个新的ArrayList数组中
		}
		//-------------------------5、生成仿真测试文件--------------------------------------------------------------------//
		writeTestPointToFile(fTestInitPoint,pGPSArrayInit);
		//将原始经纬度数据点写入仿真文件中,格式为“经度,维度”
		writeTestPointToFile(fTestFilterPoint, pGPSArrayFilterSort);
		//将过滤后的经纬度数据点写入仿真文件中,格式为“经度,维度”
		//-------------------------6、求平均误差-------------------------------------------------------------------------//
		double mDError = getMeanDistError(pGPSArrayInit,pGPSArrayFilterSort);
		//求平均误差
		System.out.println(mDError);
		//-------------------------7、求压缩率--------------------------------------------------------------------------//
		double cRate = (double)pGPSArrayFilter.size()/pGPSArrayInit.size()*100;
		//求压缩率
		System.out.println(cRate);
		//-------------------------8、生成最终结果文件--------------------------------------------------------------------//
		//将最终结果写入结果文件中,包括过滤后的点的ID,点的个数、平均误差和压缩率
		writeFilterPointToFile(oGPS,pGPSArrayFilterSort,mDError,cRate);
		//------------------------------------------------------------------------------------------------------------//
	}
	/**
*函数功能:从源文件中读出所以记录中的经纬度坐标,并存入到ArrayList数组中,并将其返回
* @param fGPS:源数据文件
* @return pGPSArrayInit:返回保存所有点坐标的ArrayList数组
* @throws Exception
*/
	public static ArrayList<ENPoint> getENPointFromFile(File fGPS)throws Exception{
		ArrayList<ENPoint> pGPSArray = new ArrayList<ENPoint>();
		if(fGPS.exists()&&fGPS.isFile()){
			InputStreamReader read = new InputStreamReader(new FileInputStream(fGPS));
			BufferedReader bReader = new BufferedReader(read);
			String str;
			String[] strGPS;
			int i = 0;
			while((str = bReader.readLine())!=null){
				strGPS = str.split(" ");
				ENPoint p = new ENPoint();
				p.id = i;
				i++;
				p.pe = (dfTodu(strGPS[3]));
				p.pn = (dfTodu(strGPS[5]));
				pGPSArray.add(p);
			}
			bReader.close();
		}
		return pGPSArray;
	}
	/**
* 函数功能:将过滤后的点的经纬度坐标、平均距离误差、压缩率写到结果文件中
* @param outGPSFile:结果文件
* @param pGPSPointFilter:过滤后的点
* @param mDerror:平均距离误差
* @param cRate:压缩率
* @throws Exception
*/
	public static void writeFilterPointToFile(File outGPSFile,ArrayList<ENPoint> pGPSPointFilter,
	double mDerror,double cRate)throws Exception{
		Iterator<ENPoint> iFilter = pGPSPointFilter.iterator();
		RandomAccessFile rFilter = new RandomAccessFile(outGPSFile,"rw");
		while(iFilter.hasNext()){
			ENPoint p = iFilter.next();
			String sFilter = p.getResultString()+"\n";
			byte[] bFilter = sFilter.getBytes();
			rFilter.write(bFilter);
		}
		String strmc = "#"+Integer.toString(pGPSPointFilter.size())+","+
		double.toString(mDerror)+","+double.toString(cRate)+"%"+"#"+"\n";
		byte[] bmc = strmc.getBytes();
		rFilter.write(bmc);
		rFilter.close();
	}
	/**
* 函数功能:将转换后的原始经纬度数据点存到文件中
* @param outGPSFile
* @param pGPSPointFilter
* @throws Exception
*/
	public static void writeInitPointToFile(File outGPSFile,ArrayList<ENPoint> pGPSPointFilter)throws Exception{
		Iterator<ENPoint> iFilter = pGPSPointFilter.iterator();
		RandomAccessFile rFilter = new RandomAccessFile(outGPSFile,"rw");
		while(iFilter.hasNext()){
			ENPoint p = iFilter.next();
			String sFilter = p.toString()+"\n";
			byte[] bFilter = sFilter.getBytes();
			rFilter.write(bFilter);
		}
		rFilter.close();
	}
	/**
* 函数功能:将数组中的经纬度点坐标数据写入测试文件中,用于可视化测试
* @param outGPSFile:文件对象
* @param pGPSPointFilter:点数组
* @throws Exception
*/
	public static void writeTestPointToFile(File outGPSFile,ArrayList<ENPoint> pGPSPointFilter)throws Exception{
		Iterator<ENPoint> iFilter = pGPSPointFilter.iterator();
		RandomAccessFile rFilter = new RandomAccessFile(outGPSFile,"rw");
		while(iFilter.hasNext()){
			ENPoint p = iFilter.next();
			String sFilter = p.getTestString()+"\n";
			byte[] bFilter = sFilter.getBytes();
			rFilter.write(bFilter);
		}
		rFilter.close();
	}
	/**
* 函数功能:将原始经纬度坐标数据转换成度
* @param str:原始经纬度坐标
* @return :返回对于的度数据
*/
	public static double dfTodu(String str){
		int indexD = str.indexOf(‘.‘);
		String strM = str.substring(0,indexD-2);
		String strN = str.substring(indexD-2);
		double d = double.parsedouble(strM)+double.parsedouble(strN)/60;
		return d;
	}
	/**
* 函数功能:保留一个double数的小数点后六位
* @param d:原始double数
* @return 返回转换后的double数
*/
	public static double getPointSix(double d){
		DecimalFormat df = new DecimalFormat("0.000000");
		return double.parsedouble(df.format(d));
	}
	/**
* 函数功能:使用三角形面积(使用海伦公式求得)相等方法计算点pX到点pA和pB所确定的直线的距离
* @param pA:起始点
* @param pB:结束点
* @param pX:第三个点
* @return distance:点pX到pA和pB所在直线的距离
*/
	public static double distToSegment(ENPoint pA,ENPoint pB,ENPoint pX){
		double a = Math.abs(geoDist(pA, pB));
		double b = Math.abs(geoDist(pA, pX));
		double c = Math.abs(geoDist(pB, pX));
		double p = (a+b+c)/2.0;
		double s = Math.sqrt(Math.abs(p*(p-a)*(p-b)*(p-c)));
		double d = s*2.0/a;
		return d;
	}
	/**
* 函数功能:用老师给的看不懂的方法求两个经纬度点之间的距离
* @param pA:起始点
* @param pB:结束点
* @return distance:距离
*/
	public static double geoDist(ENPoint pA,ENPoint pB)
	{
		double radLat1 = Rad(pA.pn);
		double radLat2 = Rad(pB.pn);
		double delta_lon = Rad(pB.pe - pA.pe);
		double top_1 = Math.cos(radLat2) * Math.sin(delta_lon);
		double top_2 = Math.cos(radLat1) * Math.sin(radLat2) - Math.sin(radLat1) * Math.cos(radLat2) * Math.cos(delta_lon);
		double top = Math.sqrt(top_1 * top_1 + top_2 * top_2);
		double bottom = Math.sin(radLat1) * Math.sin(radLat2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.cos(delta_lon);
		double delta_sigma = Math.atan2(top, bottom);
		double distance = delta_sigma * 6378137.0;
		return distance;
	}
	/**
* 函数功能:角度转弧度
* @param d:角度
* @return 返回的是弧度
*/
	public static double Rad(double d)
	{
		return d * Math.PI / 180.0;
	}
	/**
* 函数功能:根据最大距离限制,采用DP方法递归的对原始轨迹进行采样,得到压缩后的轨迹
* @param enpInit:原始经纬度坐标点数组
* @param enpArrayFilter:保持过滤后的点坐标数组
* @param start:起始下标
* @param end:终点下标
* @param DMax:预先指定好的最大距离误差
*/
	public static void TrajCompressC(ENPoint[] enpInit,ArrayList<ENPoint> enpArrayFilter,
	int start,int end,double DMax){
		if(start < end){
			//递归进行的条件
			double maxDist = 0;
			//最大距离
			int cur_pt = 0;
			//当前下标
			for (int i=start+1;i<end;i++){
				double curDist = distToSegment(enpInit[start],enpInit[end],enpInit[i]);
				//当前点到对应线段的距离
				if(curDist > maxDist){
					maxDist = curDist;
					cur_pt = i;
				}
				//求出最大距离及最大距离对应点的下标
			}
			//若当前最大距离大于最大距离误差
			if(maxDist >= DMax){
				enpArrayFilter.add(enpInit[cur_pt]);
				//将当前点加入到过滤数组中
				//将原来的线段以当前点为中心拆成两段,分别进行递归处理
				TrajCompressC(enpInit,enpArrayFilter,start,cur_pt,DMax);
				TrajCompressC(enpInit,enpArrayFilter,cur_pt,end,DMax);
			}
		}
	}
	/**
* 函数功能:求平均距离误差
* @param pGPSArrayInit:原始数据点坐标
* @param pGPSArrayFilterSort:过滤后的数据点坐标
* @return :返回平均距离
*/
	public static double getMeanDistError(
	ArrayList<ENPoint> pGPSArrayInit,ArrayList<ENPoint> pGPSArrayFilterSort){
		double sumDist = 0.0;
		for (int i=1;i<pGPSArrayFilterSort.size();i++){
			int start = pGPSArrayFilterSort.get(i-1).id;
			int end = pGPSArrayFilterSort.get(i).id;
			for (int j=start+1;j<end;j++){
				sumDist += distToSegment(
				pGPSArrayInit.get(start),pGPSArrayInit.get(end),pGPSArrayInit.get(j));
			}
		}
		double meanDist = sumDist/(pGPSArrayInit.size());
		return meanDist;
	}
}

第四部分 程序结果

4.1 程序输出结果

  压缩后的结果:

  (1)总点数:140个点;(2)平均距离误差:7.943786;(3)压缩率:4.4444%

4.2 仿真结果

  经过轨迹压缩,我们将原始经纬度坐标点转换为压缩过滤后的经纬度坐标点,我们将这两种点坐标数据分别写入两个文件中,然后根据这两个文件使用R语言进行图形绘制,分别画出压缩前和压缩后的轨迹 ,进行对比,根据对比结果就可以看出我们轨迹压缩算法是否有效,最终对比结果如图4.1所示:

Java编程实现轨迹压缩之Douglas-Peucker算法详细代码

第五部分 总结

  本程序编写过程中,遇到了各种各样的问题,也学到了很多编程经验,下面就遇到的问题及解决方案做一个总结,最后对程序存在的不足提出改进建议。

5.1 遇到的问题及解决方案

  问题1:经纬度坐标顺序问题

  解决:距离公式中的参数是纬度在前经度在后,需要调整下经纬度坐标点的顺序。

  问题2:距离不能为负值

  解决:保证求出的距离不能为负值,加绝对值函数即可。

  问题3:DP算法实现细节

  解决:开始使用ArrayList数组解决下标问题,递归求解时出现巨大误差,后来改用普通数组下标进行递归,结果好多了。

5.2 存在的不足与展望

  (1)进行轨迹压缩时,DP算法是最简单的一种算法,并不是最优的,可选用一些效果好的算法再次进行轨迹压缩;

  (2)本次实验数据的记录为3150条,数据量不算大,如果有10亿条数据,该怎么办呢?我们可以从硬件、分布式、数据预处理、数据切分、性能好的数据仓库等方面考虑。

以上就是本文关于Java编程实现轨迹压缩之Douglas-Peucker算法详细代码的全部内容,希望对大家有所帮助。如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

展开阅读
码农之家
精选文章4

8小时20分钟前整理

Java垃圾回收之标记压缩算法详解

之前写过的一篇Java垃圾回收之标记清除算法详解 ,这个算法有个缺点就是造成内存碎片,存在不连续的空间,这样会导致申请较大空间的时候,又需要进行垃圾回收。下面介绍一下标记压缩算法,可以避免内存碎片。

Java垃圾回收之标记压缩算法详解

空白部分是不连续的。

概述

这个算法的标记清除阶段,跟Java垃圾回收之标记清除算法详解  中的是一样的,而对于压缩阶段,它的工作就是移动所有的可达对象到堆内存的同一个区域中,使他们紧凑的排列在一起,从而将所有非可达对象释放出来的空闲内存都集中在一起,通过这样的方式来达到减少内存碎片的目的。如下图:

Java垃圾回收之标记压缩算法详解

压缩算法简单介绍

  • 任意顺序 : 即不考虑原先对象的排列顺序,也不考虑对象之间的引用关系,随意移动对象;
  • 线性顺序 : 考虑对象的引用关系,例如a对象引用了b对象,则尽可能将a和b移动到一块;
  • 滑动顺序 : 按照对象原来在堆中的顺序滑动到堆的一端。

优点

解决内存碎片问题。

缺点

压缩阶段,由于移动了可用对象,需要去更新引用。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对码农之家的支持。如果你想了解更多相关内容请查看下面相关链接

展开阅读

参考资料

  • 数据结构与算法Java语言描述

    数据结构与算法Java语言描述

    编辑推荐 如果你是一名正在学习计算机科学的学生,或者你是一个正在准备技术面试的软件开发者,本书将以一种更清晰、更具体,以及更吸引人的方式帮助你学习并回顾软件工程中*重要的部分-----数据结构和算法。 内容简介 本书作者强调实践知识和技能胜过理论,在书中为你展示了怎样使用数据结构实现有效的算法,并分析和测试了算法的性能。在本书中你将探索Java集合框架(JCF)中重要的类,它们是如何实现的,以及如何执行。书中的每一章都提

    大小:147 MB数据结构

    立即下载
  • Java遗传算法编程

    Java遗传算法编程

    本书简单、直接地介绍了遗传算法,并且针对所讨论的示例问题,给出了Java代码的算法实现。全书共分为6章。本书适合机器学习爱好者阅读,尤其适合对遗传算法的理论和实现感兴趣的读者阅

    大小:28.8 MBJava算法

    立即下载
  • 学习JavaScript数据结构与算法(第3版)

    学习JavaScript数据结构与算法(第3版)

    大小:13.6 MBJavaScript

    立即下载
  • 数据结构与算法经典问题解析:Java语言描述

    数据结构与算法经典问题解析:Java语言描述

    数据结构与算法经典问题解析:Java语言描述(原书第2版) 是一本数据结构方面的优秀教材,以Java为描述语言,介绍了计算机编程中使用的数据结构和算法。本书强调问题及其分析,而非理论阐

    大小:107.1 MBJava语言

    立即下载
  • 神经网络算法与实现 基于Java语言

    神经网络算法与实现 基于Java语言

    本书结合Java编程语言,由浅入深地介绍了神经网络算法的应用,涉及神经网络的构建、神经网络的结构、神经网络的学习、感知机、自组织映射等核心概念,适合对神经网络技术感兴趣的开发人员和业余读者阅读

    大小:32 MB神经网络

    立即下载
  • Java常用算法手册(第3版)

    Java常用算法手册(第3版)

    Java常用算法手册是程序设计的基础和灵魂,编程水平高低的集中体现。历经三次改版,销量达万册;完整源代码和配套视频与图书内容相辅相成。

    大小:171.5 MBJava算法

    立即下载
  • 深入Java虚拟机:JVM G1GC的算法与实现

    深入Java虚拟机:JVM G1GC的算法与实现

    编辑推荐 结合实用JVM,图解Java垃圾回收机制的关键技术! 90张图表+33段代码,轻松理解G1GC算法原理 HotSpotVM源码剖析,深入探讨G1GC具体实现 1.深入Java虚拟机底层原理,详细解读经典GC算法; 2.理论结合实际,基于HotSpotVM源码探讨具体实现; 3.图文并茂、深入浅出,辅以大量插图和代码细致讲解。 内容简介 本书深入Java虚拟机底层原理,对JVM内存管理中的垃圾回收算法G1GC进行了详细解读。全书分为算法篇和实现篇两大部分:前一部分主要介绍G1GC的算法

    大小:129 MBJava虚拟机

    立即下载