DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: jaytang
今日帖子: 0
在线用户: 4
导航: 论坛 -> 文档资料 斑竹:liumazi,ruralboy  
作者:
男 jopher3 (樵夫的马六甲) ▲▲▲▲▲ -
普通会员
2014/4/1 8:35:45
标题:
Google地图经纬度纠偏的Delphi实现 浏览:3485
加入我的收藏
楼主: 1、总体思路
    下载网上其他人提供的纠偏数据库,再规格化、排序,组织成紧凑格式的数据,以提高检索速度,然后用资源文件形式嵌入纠偏控件供使用,最后实现纠偏控件。
    纠偏处理过程主要是检索被纠偏点周边网格点的误差值、再通过插值算法计算出该点的误差值,加上原经纬度,就最终得到最终坐标。


2、纠偏数据规格化
   网上下载得到的纠偏数据比较粗放,不适合实现高速纠偏算法,比如,可能是这样的数据:
...
经度 118.6, 纬度 38.0, 经纬误差 0.0056,纬度误差 0.0005
经度 118.7, 纬度 38.0, 经纬误差 0.0058,纬度误差 0.0007
经度 118.8, 纬度 38.0, 经纬误差 0.0057,纬度误差 0.0007
经度 118.9, 纬度 38.0, 经纬误差 0.0056,纬度误差 0.0005
经度 119.0, 纬度 38.0, 经纬误差 0.0058,纬度误差 0.0007
经度 119.1, 纬度 38.0, 经纬误差 0.0061,纬度误差 0.0009
经度 119.2, 纬度 38.0, 经纬误差 0.0059,纬度误差 0.0008
...
    我们需要先将它组织成紧凑格式,以便实现高速查找:
procedure TForm1.Button1Click(Sender: TObject);
var
   SourceList: TStringList;
   tmpstr,tmp: string;
   i,j: integer;
begin
   memo1.Lines.Add('开始读原始数据文件...');
   SourceList:=TStringList.Create;
   SourceList.LoadFromFile('f:\GpsOffsetData.txt');
   memo1.Lines.Add('读原始数据文件完成,总行数='+inttostr(SourceList.Count));
   memo1.Lines.Add('开始规格化数据...');
   RecordCount:=SourceList.Count;
   SetLength(Records,SourceList.Count);
   progress.Max:=RecordCount;
   progress.Position:=0;
   MinLong:=999;
   MaxLong:=0;
   MinLat:=999;
   MaxLat:=0;
   for i := 0 to RecordCount-1 do
      begin
         tmpstr:=SourceList[i];
         //
         // 经度...
         j:=pos(',',tmpstr);
         tmp:=copy(tmpstr,1,j-1);
         delete(tmpstr,1,j);
         j:=pos(' ',tmp);
         delete(tmp,1,3);
         Records[i].Long:=round(strtofloat(tmp)*10);
         //
         // 纬度...
         j:=pos(',',tmpstr);
         tmp:=copy(tmpstr,1,j-1);
         delete(tmpstr,1,j);
         j:=pos(' ',tmp);
         delete(tmp,1,3);
         Records[i].Lat:=round(strtofloat(tmp)*10);
         //
         // 经度偏差...
         j:=pos(',',tmpstr);
         tmp:=copy(tmpstr,1,j-1);
         delete(tmpstr,1,j);
         j:=pos(' ',tmp);
         delete(tmp,1,5);
         Records[i].LongOffset:=round(strtofloat(tmp)*10000);
         //
         // 纬度偏差...
         j:=pos(' ',tmpstr);
         delete(tmpstr,1,5);
         Records[i].LatOffset:=round(strtofloat(tmpstr)*10000);
         //
         // 得到经纬度总范围...
         if MinLong>Records[i].Long then
          MinLong:=Records[i].Long;
         if MaxLong<Records[i].Long then
          MaxLong:=Records[i].Long;
         if MinLat>Records[i].Lat then
          MinLat:=Records[i].Lat;
         if MaxLat<Records[i].Lat then
          MaxLat:=Records[i].Lat;
         //
         memo1.Lines.Add('Lon_g='+inttostr(Records[i].Long)+'  Lat='+inttostr(Records[i].Lat)+'  Lon_gOffset='+inttostr(Records[i].LongOffset)+'  LatOffset='+inttostr(Records[i].LatOffset));
         Progress.Position:=i+1;
      end;
   memo1.Lines.Add('规格化数据完成!');
   memo1.Lines.Add('记录总数='+inttostr(RecordCount)+'  占用内存='+inttostr(RecordCount*8)+'Bytes.');
   memo1.Lines.Add('经度范围:'+inttostr(minlong)+'--'+inttostr(maxlong));
   memo1.Lines.Add('纬度范围:'+inttostr(minlat)+'--'+inttostr(maxlat));
end;

procedure TForm1.Button3Click(Sender: TObject);
var
   i: integer;
begin
   SysDataset.CommandText:='SELECT * FROM GOOGLEMAPCORRECT';
   SysDataset.Active:=true;
   progress.Max:=RecordCount;
   progress.Position:=0;
   for i := 0 to RecordCount-1 do
      begin
         SysDataset.Append;
         SysDataset.FieldValues['Long']:=Records[i].Long;
         SysDataset.FieldValues['Lat']:=Records[i].Lat;
         SysDataset.FieldValues['LongError']:=Records[i].LongOffset;
         SysDataset.FieldValues['LatError']:=Records[i].LatOffset;
         SysDataset.Post;
         Progress.Position:=i+1;
      end;
   SysDataset.Active:=false;
   memo1.Lines.Add('数据已经保存到数据库!');
end;

procedure TForm1.Button4Click(Sender: TObject);
var
   i: integer;
   Long,Lat: word;
   LongError,LatError: smallint;
   Stream: TMemoryStream;
begin
   SysDataset.CommandText:='SELECT * FROM GOOGLEMAPCORRECT ORDER BY LONG,LAT';
   SysDataset.Active:=true;
   progress.Max:=SysDataset.RecordCount;
   progress.Position:=0;
   Stream:=TMemoryStream.Create;
   while not sysdataset.Eof do
      begin
         Long:=word(SysDataset.FieldByName('Long').AsInteger);
         Lat:=word(SysDataset.FieldByName('Lat').AsInteger);
         Longerror:=smallint(SysDataset.FieldByName('LongError').AsInteger);
         Laterror:=smallint(SysDataset.FieldByName('LatError').AsInteger);
         Stream.Write(long,2);
         Stream.Write(lat,2);
         Stream.Write(longerror,2);
         Stream.Write(laterror,2);
         SysDataset.Next;
         Progress.Position:=Progress.Position+1;
      end;
   SysDataset.Active:=false;
   Stream.Position:=0;
   Stream.SaveToFile('f:\gmapcorrect.dat');
   FreeANdNil(Stream);
   memo1.Lines.Add('规格化排序后数据已经导出到文件!');
end;


3、插值计算算法
   GIS里常用的是反距离加权算法。于是写一个,主要计算函数:
//
// 插值计算...
function TIDWInterpolation.Interpolating(const x: double; const y: double): double;
var
   a,b: double;
   i: integer;
begin
   a:=0;
   b:=0;
   try
      for i := 0 to PointCount-1 do
         begin
          if (points[i].x=x) and (points[i].y=y) then
          begin
          result:=points[i].z;
          exit;
          end;
          a:=a+(1/((x-Points[i].x)*(x-Points[i].x)+(y-points[i].y)*(y-points[i].y)))*points[i].z;
          b:=b+1/((x-Points[i].x)*(x-Points[i].x)+(y-points[i].y)*(y-points[i].y));
         end;
      if b=0 then
         result:=0
      else
         result:=a/b;
   except
      result:=0;
   end;
end;

先来测试一下此算法的正确性:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   FreeAndNil(IDW);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   idw:=TIDWInterpolation.Create;
   idw.AddPoint(0,0,20);
   idw.AddPoint(400,0,5);
   idw.AddPoint(0,300,15);
   idw.AddPoint(400,300,-2);
end;

procedure TForm1.Shape1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
   z: double;
begin
   z:=idw.Interpolating(x,y);
   edit5.Text:=inttostr(x)+','+inttostr(y)+','+floattostr(z);
end;

效果如下,通过鼠标移到不同位置、观察计算结果,可以看出完全符合“反距离加权”原则:
此帖子包含附件:
JPEG 图像
大小:23.4K
----------------------------------------------
樵夫的大马甲
作者:
男 jopher3 (樵夫的马六甲) ▲▲▲▲▲ -
普通会员
2014/4/1 8:39:02
1楼: 4、地图纠偏算法的实现
   按以下几个步骤处理:
a、经度纬度超出中国范围的,返回原值
b、根据经纬度确定所属小矩形区域四顶点经纬度
c、检索得到四顶点偏差值
d、使用插值算法计算经纬度偏差
e、原经纬度+偏差,得到纠偏结果

主要代码:
//
// 查找某经度起始记录...
function TGMapCorrect.FindBeginIndex(aLong: word): integer;
var
   l,h,m: integer;
begin
   result:=-1;
   l:=0;
   h:=PointCount-1;
   while l<=h do
      begin
         m:=(l+h) div 2;
         //
         // 找到...
         if aLon_g=Points[m].Long then
          begin
          //
          // 确定起始记录...
          result:=m;
          l:=m-1;
          while points[l].lon_g=aLong do
          begin
          dec(result);
          dec(l);
          end;
          break;
          end;
         //
         // 修正查找范围...
         if Points[m].Long>aLong then
          h:=m-1
         else
          l:=m+1;
      end;
end;

//
// 纠偏...
function TGMapCorrect.Correct(const Long: double; const Lat: double; var NewLong: double; var NewLat: double): boolean;
var
   MinLong,MaxLong: word;
   MinLat,MaxLat: word;
   i,j: integer;
begin
   result:=true;
//
// 假如经度超出,返回原值...
   if (Long<=75) or (Long>=131.9) then
      begin
         NewLong:=Long;
         NewLat:=Lat;
         exit;
      end;
//
// 假如纬度超出,返回原值...
   if (Lat<=21) or (Lat>=51.9) then
      begin
         NewLong:=Long;
         NewLat:=Lat;
         exit;
      end;
//
// 根据经纬度确定所属小矩形区域四顶点经纬度...
   MinLong:=word(trunc(Long*10));
   MaxLong:=MinLong+1;
   MinLat:=word(trunc(Lat*10));
   MaxLat:=MinLat+1;
//
// 检索得到四顶点偏差值...
   j:=FindBeginIndex(MinLong);
   if j=-1 then
      begin
         NewLong:=Long;
         NewLat:=Lat;
         result:=false;
         exit;
      end;
//
// 使用插值算法计算经纬度偏差...
   IDWI1.initialize;
   IDWI2.initialize;
   for i:= j to PointCount-1 do
      begin
         //
         // 找到第一点...
         if (Points[i].Lon_g=MinLong) and (Points[i].Lat=MinLat) then
          begin
          IDWI1.AddPoint(MinLong,MinLat,Points[i].LongError);
          IDWI2.AddPoint(MinLong,MinLat,Points[i].LatError);
          end;
         //
         // 找到第二点...
         if (Points[i].Lon_g=MinLong) and (Points[i].Lat=MaxLat) then
          begin
          IDWI1.AddPoint(MinLong,MaxLat,Points[i].LongError);
          IDWI2.AddPoint(MinLong,MaxLat,Points[i].LatError);
          end;
         //
         // 找到第三点...
         if (Points[i].Lon_g=MaxLong) and (Points[i].Lat=MinLat) then
          begin
          IDWI1.AddPoint(MaxLong,MinLat,Points[i].LongError);
          IDWI2.AddPoint(MaxLong,MinLat,Points[i].LatError);
          end;
         //
         // 找到第四点...
         if (Points[i].Lon_g=MaxLong) and (Points[i].Lat=MaxLat) then
          begin
          IDWI1.AddPoint(MaxLong,MaxLat,Points[i].LongError);
          IDWI2.AddPoint(MaxLong,MaxLat,Points[i].LatError);
          break;
          end;
      end;
//
// 原经纬度+偏差,得到纠偏结果...
   NewLong:=Long+IDWI1.Interpolating(Long*10,Lat*10)/10000;
   NewLat:=Lat+IDWI2.Interpolating(Long*10,Lat*10)/10000;
end;


看纠偏前后的坐标对比:
此帖子包含附件:
JPEG 图像
大小:62.7K
----------------------------------------------
樵夫的大马甲
作者:
男 jopher3 (樵夫的马六甲) ▲▲▲▲▲ -
普通会员
2014/4/1 8:39:28
2楼: 纠偏后的:
此帖子包含附件:
JPEG 图像
大小:60.6K
----------------------------------------------
樵夫的大马甲
作者:
男 jopher3 (樵夫的马六甲) ▲▲▲▲▲ -
普通会员
2014/4/1 8:40:37
3楼: 5、结论
   从纠偏效果来看,完全符合预期,精度大约在1-20米之间
----------------------------------------------
樵夫的大马甲
作者:
男 cjandy (cjandy) ★☆☆☆☆ -
盒子活跃会员
2014/4/1 8:47:31
4楼: 好文章,非常感谢
----------------------------------------------
-
作者:
男 sbzldlb (边缘人) ★☆☆☆☆ -
盒子活跃会员
2014/4/1 8:56:23
4楼: 不错支持
----------------------------------------------
Delphi Android移动开发讨论论坛 老大富翁论坛历史数据搜索引擎
作者:
男 nerika (非常可樂) ★☆☆☆☆ -
普通会员
2014/4/1 9:04:12
5楼: 砍柴的, 牛X, 绝对支持一下

按此在新窗口浏览图片
----------------------------------------------
-
作者:
男 conch (conch) ★☆☆☆☆ -
盒子活跃会员
2014/4/1 9:39:24
6楼: 好贴,顶起
----------------------------------------------
-
作者:
男 delphiilove (乌羽玉) ★☆☆☆☆ -
普通会员
2014/4/1 16:18:20
7楼: 好帖,支持
----------------------------------------------
-
作者:
男 crystalmoon (crystalmoon) ★☆☆☆☆ -
盒子活跃会员
2014/4/1 16:38:52
8楼: ,支持
----------------------------------------------
-
作者:
男 bdl1 (bdl1) ▲▲▲▲▲ -
普通会员
2014/4/1 16:44:35
9楼: 准备购买你的产品了!
----------------------------------------------
-我的博客
作者:
男 vclclx (vclclx) ★☆☆☆☆ -
普通会员
2014/4/1 18:40:48
10楼: 牛X
----------------------------------------------
-
作者:
男 tintin1943 (零输好) ★☆☆☆☆ -
盒子活跃会员
2014/4/1 18:46:02
11楼: 樵夫你应写点博客,把闲散的文章汇集一起,顺便版本发布,神秘嘉宾,抽奖啊
----------------------------------------------
不喧哗 自有声 心静 思远 志行千里
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行120.1172毫秒 RSS