• 在Web客户端中基于Proj4实现坐标转换


    当鼠标滑过地图,我们会扫一眼鼠标的地理位置,至少要能看到经纬度,好确认当前的范围和地物是否处在正常的位置。这对于C/S应用来说,是最为常见的辅助功能,即使是在B/S中,这似乎也不是难事,比如谷歌地图等都能提供这个功能,但是我们也知道,这些地图数据是固定投影的,获取经纬度坐标的途径是单一的,而这也不是本文要考虑的内容。

    在系统应用中,B/S结构的GIS系统会发布具有不同投影类型的地图数据,而在客户端系统中为了显示鼠标的当前地理位置信息,通常我们会有三种解决办法:第一种,直接将鼠标的屏幕坐标转换为经纬度坐标并显示——仅限于发布的地图采用了经纬度坐标系;第二种,鼠标MouseMove过程中与服务器进行实时通信,将鼠标坐标转换为经纬度后返回给客户端显示——这要取决于网络是否稳定;第三种,则是在客户端进行坐标投影转换,相对来说,前两种解决办法都有一定的限制,而第三种解决办法则相对通用,也就是本文所讨论的话题。

           当然,在开始讨论前,先明确一下有关的背景知识。坐标投影转换的资料,参考LionGG的文章http://hi.baidu.com/liongg/blog/item/81d7b48f8bdfb0ebf01f36fd.html,内容很详尽。有关Proj4http://trac.osgeo.org/proj/,是著名的开源C语言投影库,现在也包括了Javascript版(proj4js),ActionScript版(proj4as)等语言的实现。有关各种投影的参数定义,可参考http://spatialreference.org,如果已经有定义,例如EPSG:2334:Xian 1980,可以搜索并查看其定义,我们能看到投影信息的多种表述,如proj4类型的表述为http://spatialreference.org/ref/epsg/2334/proj4/)。


     (http://www.proj4js.org/坐标转换图)

           本文主要针对客户端坐标投影转换的实现,通过实践SuperMap iClient for Flex与Openscales-proj4as,整理该流程如下:

    1、获取openscales-proj4as-1.2.1.swc,当然现在以后会有更新版本,或者直接获取openscales的整个库——有API文档和源码,本文中直接使用源代码进行调试;

    2、获取SuperMap iClient for Flex,当然也要有配套的SuperMap iServer,为了配合本次实践,这里发布了一个China400的地图,采用了Lambert Conformal Conic投影,中央经线104°,第一标准纬线24°,第二标准纬线40°,单位米; 

    1 <supermap:Map id="myMap">

    2     <supermap1:TiledDynamicRESTLayer id="layer" url="http://localhost:8090/iserver/services/LCC/rest/maps/LCC" />
    3 </supermap:Map>
    4 <s:Label id="coordInfo"  width="400" height="100" top="10" left="350" text="" />

    效果如下图

     

     3、现在利用Label显示鼠标的坐标位置看看效果。

      1 private function init():void

     2 {
     3     this.myMap.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
     4 }
     5 private function mouseMoveHandler(event:MouseEvent):void
     6 {
     7     var point:Point = new Point(event.stageX, event.stageY);
     8     var point2D:Point2D = this.myMap.screenToMap(point);
     9     this.coordInfo.text = point2D.x + "  " + point2D.y;
    10 }

     效果如图


    这个坐标的单位是米,对于我们来说这个坐标没能带来实际意义,我们还是要靠经纬度坐标来判断地理位置的。

    4、现在我们使用openscales-proj4as来实现坐标转换的工作。想要熟悉openscales-proj4as的接口不是那么容易,API接口文档轻描淡写,如果不是GIS或测绘专业的人,是较难理解其含义的。现有的两个Package中,org.openscales.proj4as是主要的投影基础类定义,而org.openscales.proj4as.proj则是一些预定义的投影,见下表:

    Package

    Class

    Description

    org.openscales.proj4as

    Datum

    大地基准面,例如北京54

    Proj4as

    主类,提供静态方法做投影转换,transform方法是将一种投影类型的坐标转换为另一种投影类型的坐标值

    ProjConstants

    常量,其中有一些很实用

    ProjPoint

    坐标点

    ProjProjection

    投影类,任何投影的定义都可以继承该类,defs静态常量可以通过键值定义投影类型,forward方法是将经纬度坐标转换为投影坐标,inverse方法是将投影坐标转换为经纬度坐标

    org.openscales.proj4as.proj

    ProjParams

    投影参数类,用于初始化各种预定义的投影

    ProjLcc

    预定义的兰伯特投影类

    其他投影类

     基于上表的描述就可以定义投影了,这里定义兰伯特投影的方式有两种,如下

     1 private static var projLcc:Object;

      1 /**

     2  * 通过投影参数定义北京54兰伯特投影
     3  * */
     4 private function defsProjLcc1():void
     5 {
     6     if(!projLcc)
     7     {
     8         //使用系统常量的定义Datum(大地基准面),Krassovsky 1942
     9         var krass:Object                    = ProjConstants.Ellipsoid["krass"];
    10         var projLccData:ProjParams          = new ProjParams();
    11         //椭球长半轴
    12         projLccData.a                       = krass.a;
    13         //扁率
    14         projLccData.rf                      = krass.rf;
    15         //椭球长半轴,这里必须设置b,究其原因是功能接口没有利用a、b、rf关系做相互计算
    16         projLccData.b                       = projLccData.a*(1- 1/projLccData.rf);
    17         //投影坐标系单位
    18         projLccData.units                   = "m";
    19         //投影原点纬线
    20         projLccData.lat0                    = 0.00000000;
    21         //投影中央经线
    22         projLccData.long0                   = 104 * Math.PI/180;
    23         //第一标准纬线
    24         projLccData.lat1                    = 24 * Math.PI/180;
    25         //第二标准纬线
    26         projLccData.lat2                    = 40 * Math.PI/180;
    27         //水平偏移量
    28         projLccData.x0                      = 0.000;
    29         //垂直偏移量
    30         projLccData.y0                      = 0.000;
    31         //使用投影参数实例化投影对象
    32         projLcc                             = new ProjLcc(projLccData);
    33         //初始化投影参数,必须执行该接口
    34         projLcc.init();
    35     }
    36 }

      1 /**

     2  * 定义北京54兰伯特投影,
     3  * ProjProjection.defs[]静态常量定义键值对,
     4  * 10010为自定义的EPSG值
     5  * 键值内容参考投影参数
     6  * */
     7 private function defsProjLcc2():void
     8 {
     9     if(!projLcc) 
    10     {
    11         if(!ProjProjection.getProjProjection("10010"))
    12         {
    13             ProjProjection.defs["10010"]    = "+title=Beijing1954 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378245.0000 +rf=298.3 +lat_0=0.00000000 +lon_0=104.000000000 +lat_1=24.000000000 +lat_2=40.000000000 +x_0=0.000 +y_0=0.000 +units=m +no_defs";
    14         }
    15         projLcc = ProjProjection.getProjProjection("10010");
    16     }
    17 }

     接着获取鼠标的当前坐标信息,并转换为地理坐标,并采用投影转换的方法进行坐标转换,如下


     private function mouseMoveHandler(event:MouseEvent):void

    {
        var point:Point = new Point(event.stageX, event.stageY);
        var point2D:Point2D 
    = this.myMap.screenToMap(point);
        var sourceP:GeoPoint 
    = new GeoPoint(point2D.x,point2D.y);
        var destP:GeoPoint 
    = coordTransform(sourceP);
        
    this.coordInfo.text = destP.x.toPrecision(8+ "  " + destP.y.toPrecision(8);
    }
     1 private function coordTransform(sourceP:GeoPoint):GeoPoint
     2 {
     3     if(sourceP&&projLcc)
     4     {
     5         var sourceP2:ProjPoint                 = new ProjPoint(sourceP.x, sourceP.y);
     6         var destP:ProjPoint                     = projLcc.inverse(sourceP2);
     7         var rp:GeoPoint                         = new GeoPoint(destP.x * 180/Math.PI,destP.y * 180/Math.PI);
     8         return rp; 
     9     }
    10     
    11     return null;

    12 } 

    转换后的效果如下图

     

     5、至此,本文就完成了基本的坐标转换,同时经过简单的测试,发现连续转换10000000对坐标点(兰伯托投影)共耗时17.689s,1000对则耗时2ms,性能适中,可以考虑做客户端地图动态投影。

    结束之前,插上一句,不论是投影之间的坐标转换实现动态投影,还是投影的经纬度转换,都有应用价值,也希望各种客户端应用系统都能结合Proj4实现坐标转换。 

    Author:dulvyizhihua
  • 相关阅读:
    font-weight 导致iconfont显示有问题和糊
    compositionstart与compositionend解决组合文字输入问题
    工具函数 createOnceHandler 只执行一次的事件绑定
    redis的基本数据类型
    dubbo服务的发布和调用
    Dubbo介绍和服务架构分析
    spring的事务配置方法
    [已解决] java.net.ConnectException: Connection refused: no further information
    [已解决] 点击 【Show in system explorer】Eclipse卡死,未响应
    [已解决] java.lang.ClassNotFoundException: org.apache.jsp.WEB_002dINF.com.yourproject.test_jsp
  • 原文地址:https://www.cnblogs.com/dulvyizhihua/p/2077853.html
Copyright © 2020-2023  润新知