2

我想将英国OSGB 36坐标转换为WGS 84(即“标准”纬度和经度),以便将它们绘制成 Google 地球的 KML 文件。

解决此问题的最佳方法是什么?我在 VB .NET 中实现。

我可能应该补充一点,我的问题不是“如何编写 KML 文件?”。我的问题是“如何在这两个坐标系之间进行转换?”!!

我希望有一个我可以使用的库,而不是滚动我自己的函数——这似乎是其他人会实现的那种东西。

4

5 回答 5

2

我工作的公司刚刚开源了一个库来做到这一点:http ://code.google.com/p/geocoordconversion/

于 2010-03-10T16:18:39.547 回答
1

您需要实现Helmert 转换。我用 Javascript 写了一个转换,你可以适应它。脚本用于 WGS84-OSGB36 转换的算法源自获得许可的 OSGB 电子表格。对于英国 90% 的地区,转换精度在 7m 左右,应该与典型 GPS 接收器进行的转换相似。

有关更多详细信息,请参阅文档源代码

编辑:您可能想查看这个包含源代码的OCX 。

于 2009-03-18T14:28:37.293 回答
1

首先,根据从OSGB 36链接的这个页面:

误区 4:“坐标系之间有精确的数学公式可以改变”</p>

其次,来自同一链接:“从一个坐标系到另一个坐标系:大地转换”包括“近似 WGS84 到 OSGB36/ODN 转换”部分

编辑:注意,当操作系统说“近似”时,它们的意思是错误> 5m。

于 2009-03-18T14:29:47.917 回答
1
//=======================================================================
/* Project: Geocode.Service
*  Author: Steve Loughran
*  Copyright (C) 2000 Steve Loughran
*  See license.txt or license.html for license and copyright information 
*  RCS $Header: /cvsroot/geocode/geocode.net/src/library/Osgb.cs,v 1.4 2002/04/23 05:12:37 steve_l Exp $
*  jedit:mode=csharp:tabSize=4:indentSize=4:syntax=true:
*/
//=======================================================================


using System;

namespace net.sourceforge.geocode {

/// <summary>
/// OSGB conversion routines. xlated from C++ to Java to C#
/// </summary>

public class OsgbGridReference: GeoMath
{

private string _gridSquare="";
private long _easting=0;
private long _northing=0;

/// <summary>
/// empty constructor
/// </summary>
public OsgbGridReference() {}

/// <summary>
/// constructor from gridref
/// </summary>
public OsgbGridReference(String gridSquare, 
  long easting,
  long northing) {
 SetGridRef(gridSquare,northing,easting);
}

/// <summary>
/// constructor from co-ordinates
/// </summary>
public OsgbGridReference(double latitude, double longitude) {
 SetPosition(latitude,longitude);
}

/// <summary>
/// constructor from position
/// </summary>
public OsgbGridReference(Position position)
 : this(position.Latitude,position.Longitude) {
}

/// <summary>grid square property</summary>    
public string GridSquare {
 get {return _gridSquare;}
 set {_gridSquare=value;}
}
/// <summary>northing property</summary>    

public long Northing {
 get {return _northing;}
 set {_northing=value;} 
}

/// <summary>easting property</summary>    
public long Easting {
 get {return _easting;}
 set {_easting=value;} 
}

/// <summary>
/// set the grid refernce
/// </summary>
/// <returns> </returns>

public void SetGridRef(String gridSquare, 
  long northing, 
  long easting) {
 _gridSquare=gridSquare;
 _northing=northing;
 _easting=easting;
}

/// <summary>
///  rudimentary validity test. A better one is to
/// extract the position
/// </summary>
/// <returns>true iff there is a gridsquare </returns>

public bool IsValid() 
 {return _gridSquare.Length==2;}


/// <summary>
/// get a position object from a location
/// </summary>
/// <returns> Position </returns>

public Position ToPosition() {
 double lat,lon;
 ConvertOSGBtoLL(_gridSquare,_northing,_easting,
   out lat, out lon);
 Position p=new Position(lat,lon);
 p.Name=ToString();
 return p;
}   

/// <summary>
/// set a gridref from a lat/long tuple
/// </summary>
/// <returns> success flag </returns>

public bool SetPosition(double latitude, double longitude) {
 _gridSquare=ConvertLLtoOSGB(latitude,
   longitude,
   out _northing,
   out _easting);
 return IsValid();
}

/// <summary>
/// set a gridref from a position
/// </summary>
/// <returns> success flag </returns>

public bool SetPosition(Position position) {
 return SetPosition(position.Latitude,position.Longitude);
}

/// <summary>
///  stringify
/// </summary>

public override string ToString() {
 return String.Format("{0} {1:000} {2:000}",
  _gridSquare,Easting,Northing);
}

/// <summary>
///  equality test: works on lat and long
/// </summary>

public override bool Equals(object o) {
 OsgbGridReference pos=(OsgbGridReference)o;
 return _gridSquare==pos._gridSquare &&
  _northing==pos._northing &&
  _easting==pos._easting;
}

/// <summary>
/// hash code builder
/// </summary>

public override int GetHashCode() {
        return (int)(_easting+_northing); 
}

/// <summary>
/// determines base co-ordinates of a square like "ST"
/// </summary>
///    <parameter name="OSGBGridSquare"> square </parameter>
///    <parameter name="easting"> easting</parameter>  
///    <parameter name="northing"> northing</parameter> 
/// <returns>true if the coordinates were in range</returns>

static bool ConvertOSGBSquareToRefCoords(string OSGBGridSquare,
         out long  easting,
                           out long  northing) {
 int pos, x_multiplier=0, y_multiplier=0;
 string GridSquare = "VWXYZQRSTULMNOPFGHJKABCDE";
 bool trouble=false;
 long east,north;
 easting=northing=0;
 //find 500km offset
    OSGBGridSquare=OSGBGridSquare.ToUpper();
 char ch = OSGBGridSquare[0];
 switch(ch) {
  case 'S': x_multiplier = 0; y_multiplier = 0; break;
  case 'T': x_multiplier = 1; y_multiplier = 0; break;
  case 'N': x_multiplier = 0; y_multiplier = 1; break;
  case 'O': x_multiplier = 1; y_multiplier = 1; break;
  case 'H': x_multiplier = 0; y_multiplier = 2; break;
  case 'J': x_multiplier = 1; y_multiplier = 2; break;
        default: trouble=true; break;
 }
    if(!trouble) {
  east=x_multiplier * 500000L;
  north=y_multiplier * 500000L;
  //find 100km offset and add to 500km offset to get coordinate of
  //square point is in
  char subsquare=OSGBGridSquare[1];
  pos = GridSquare.IndexOf(subsquare);
     if(pos>-1) {
   east += ((pos % 5) * 100000L);
   north += ((pos / 5) * 100000L);
     easting=east;
   northing=north;
     }
     else {
         trouble=true;
     }
    }
    return !trouble;
}

///<summary>
///convert a internal OSGB coord to lat/long
///Equations from USGS Bulletin 1532
///East Longitudes are positive, West longitudes are negative.
///North latitudes are positive, South latitudes are negative
///Lat and Long are in decimal degrees.
///Written by Chuck Gantz- chuck.gantz@globalstar.com
///</summary>
///    <parameter name="OSGBEasting">easting </parameter> 
///   <parameter name="OSGBEasting">northing </parameter> 
///   <parameter name="OSGBZone"> gridsquare</parameter>  
///   <parameter name="latitude"> latitude</parameter> 
///   <parameter name="longitude"> longitude</parameter> 

static void ConvertOSGBtoLL(string OSGBZone,
   double OSGBNorthing,
   double OSGBEasting,
   out double latitude,
   out double longitude) {
 double k0 = 0.9996012717;
 double a;
 double eccPrimeSquared;
 double N1, T1, C1, R1, D, M;
 double LongOrigin = -2;
 double LatOrigin = 49;
 double LatOriginRad = LatOrigin * deg2rad;
 double mu, phi1, phi1Rad;
 double x, y;
    long northing;
    long easting;

 //Airy model of the ellipsoid.
 double majoraxis = a = 6377563.396;
 double minoraxis = 6356256.91;
 double eccSquared = (majoraxis * majoraxis - minoraxis * minoraxis) /
       (majoraxis * majoraxis);
 double e1 = (1-sqrt(1-eccSquared))/(1+sqrt(1-eccSquared));
 //only calculate M0 once since it is based on the origin of the OSGB projection, which is fixed
 double  M0 = a*((1 - eccSquared/4 - 3*eccSquared*eccSquared/64 - 5*eccSquared*eccSquared*eccSquared/256)*LatOriginRad
    - (3*eccSquared/8 + 3*eccSquared*eccSquared/32 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatOriginRad)
    + (15*eccSquared*eccSquared/256 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatOriginRad)
    - (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatOriginRad));
 ConvertOSGBSquareToRefCoords(OSGBZone, out easting, out northing);
 //remove 400,000 meter false easting for longitude
 x = OSGBEasting - 400000.0 + easting;
 //remove 100,000 meter false easting for longitude
 y = OSGBNorthing + 100000.0 + northing;
 eccPrimeSquared = (eccSquared)/(1-eccSquared);
 M = M0 + y / k0;
 mu = M/(a*(1-eccSquared/4-3*eccSquared*eccSquared/
      64-5*eccSquared*eccSquared*eccSquared/256));
 phi1Rad = mu + (3*e1/2-27*e1*e1*e1/32)*sin(2*mu)
    + (21*e1*e1/16-55*e1*e1*e1*e1/32)*sin(4*mu)
    +(151*e1*e1*e1/96)*sin(6*mu);
 phi1 = phi1Rad*rad2deg;
 N1 = a/sqrt(1-eccSquared*sin(phi1Rad)*sin(phi1Rad));
 T1 = tan(phi1Rad)*tan(phi1Rad);
 C1 = eccPrimeSquared*cos(phi1Rad)*cos(phi1Rad);
 R1 = a*(1-eccSquared)/pow(1-eccSquared*sin(phi1Rad)*sin(phi1Rad), 1.5);
 D = x/(N1*k0);
 latitude = phi1Rad - (N1*tan(phi1Rad)/R1)*(D*D/2-(5+3*T1+10*C1-4*C1*C1-9*eccPrimeSquared)*D*D*D*D/24
     +(61+90*T1+298*C1+45*T1*T1-252*eccPrimeSquared-3*C1*C1)*D*D*D*D*D*D/720);
 latitude *= rad2deg;
 longitude = (D-(1+2*T1+C1)*D*D*D/6+(5-2*C1+28*T1-3*C1*C1+8*eccPrimeSquared+24*T1*T1)
     *D*D*D*D*D/120)/cos(phi1Rad);
 longitude = LongOrigin + longitude * rad2deg;
}


/// <summary>
/// converts lat/long to OSGB coords.  Equations from USGS Bulletin 1532
/// East Longitudes are positive, West longitudes are negative.
/// North latitudes are positive, South latitudes are negative
/// Lat and Long are in decimal degrees
/// </summary>
///  Written by Chuck Gantz- chuck.gantz@globalstar.com
///   <parameter name="latitude"> IN latitude</parameter> 
///   <parameter name="longitude">IN longitude </parameter> 
///   <parameter name="OSGBEasting"> OUT easting</parameter> 
///  <parameter name="OSGBNorthing"> OUT northing</parameter> 

static public string ConvertLLtoOSGB(double latitude,
    double longitude,                
                out long OSGBNorthing,
    out long OSGBEasting) {
 double a;
 double eccSquared;
 double k0 = 0.9996012717;
 double LongOrigin = -2;
 double LongOriginRad = LongOrigin * deg2rad;
 double LatOrigin = 49;
 double LatOriginRad = LatOrigin * deg2rad;
 double eccPrimeSquared;
 double N, T, C, A, M;
 double LatRad = latitude*deg2rad;
 double LongRad = longitude*deg2rad;
 double easting, northing;
 double majoraxis = a = 6377563.396;//Airy
 double minoraxis = 6356256.91;//Airy
 eccSquared = (majoraxis * majoraxis - minoraxis * minoraxis) /
       (majoraxis * majoraxis);
 //only calculate M0 once since it is based on the origin
 //of the OSGB projection, which is fixed
 double  M0 = a*((1  - eccSquared/4 - 3*eccSquared*eccSquared/64 - 5*eccSquared*eccSquared*eccSquared/256)*LatOriginRad
    - (3*eccSquared/8 + 3*eccSquared*eccSquared/32 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatOriginRad)
    + (15*eccSquared*eccSquared/256 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatOriginRad)
    - (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatOriginRad));
 eccPrimeSquared = (eccSquared)/(1-eccSquared);
 N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad));
 T = tan(LatRad)*tan(LatRad);
 C = eccPrimeSquared*cos(LatRad)*cos(LatRad);
 A = cos(LatRad)*(LongRad-LongOriginRad);
 M = a*((1 - eccSquared/4 - 3*eccSquared*eccSquared/64- 5*eccSquared*eccSquared*eccSquared/256)*LatRad
    - (3*eccSquared/8 + 3*eccSquared*eccSquared/32 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatRad)
    + (15*eccSquared*eccSquared/256 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatRad)
    - (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatRad));
 easting = (double)(k0*N*(A+(1-T+C)*A*A*A/6
    + (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120));
 easting += 400000.0; //false easting
 northing = (double)(k0*(M-M0+N*tan(LatRad)*(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24
     + (61-58*T+T*T+600*C-330*eccPrimeSquared)*A*A*A*A*A*A/720)));
 northing -= 100000.0;//false northing
 return ConvertCoordsToOSGBSquare(easting, northing,out OSGBEasting, out OSGBNorthing);
}


/// <summary>
/// convert a internal OSGB coord to a gridsquare and internal values.
/// </summary>
///    <parameter name="easting"> easting</parameter> 
///    <parameter name="northing"> northing</parameter>
///    <parameter name="OSGBEasting">OSGBEasting out </parameter>
///    <parameter name="OSGBNorthing">OSGBNorthing out </parameter>

static string ConvertCoordsToOSGBSquare(double easting,
     double northing,
                    out long  OSGBEasting,
                    out long  OSGBNorthing)
{
 string GridSquare = "VWXYZQRSTULMNOPFGHJKABCDE";
 long posx, posy; //positions in grid
 OSGBEasting = (long)(easting + 0.5); //round to nearest int
 OSGBNorthing = (long)(northing + 0.5); //round to nearest int
 string OSGBGridSquare="";

 //find correct 500km square
 posx = OSGBEasting / 500000L;
 posy = OSGBNorthing / 500000L;
 if(posx<0 || posx>4 || posy<0 || posy>4)
  return "";
 long offset=posx + posy * 5 + 7;
    OSGBGridSquare+= GridSquare[(int)offset];

 //find correct 100km square
 posx = OSGBEasting % 500000L;//remove 500 km square
 posy = OSGBNorthing % 500000L;//remove 500 km square
 posx = posx / 100000L;//find 100 km square
 posy = posy / 100000L;//find 100 km square
 if(posx<0 || posx>4 || posy<0 || posy>4)
  return "";
 offset=posx + posy * 5; 
 OSGBGridSquare+= GridSquare[(int)offset];

 //remainder is northing and easting
 OSGBNorthing = OSGBNorthing % 500000L;
 OSGBNorthing = OSGBNorthing % 100000L;
 OSGBEasting = OSGBEasting % 500000L;
 OSGBEasting = OSGBEasting % 100000L;
    return OSGBGridSquare;
}



/// <summary>
/// turn the latitude and longitude into a string
/// </summary>
///    <parameter name="latitude"> lat</parameter>
///    <parameter name="longitude"> long</parameter> 
///    <parameter name="infill"> text between coordinates</parameter>  
///    <returns>return something like E004 N123</returns>

static string GetSensibleLatLongstring(double latitude,
  double longitude,
        int decimals,
        string infill) {
    bool bNorth=latitude>=0;
 bool bWest=longitude<=0;
    latitude=Math.Abs(latitude);
    longitude=Math.Abs(longitude);
    double multiplier=(int)pow(10,decimals);
 int hiLat=(int)latitude;
    int lowLat=(int)((latitude-hiLat)*multiplier);
    double newLat=lowLat/multiplier+hiLat;
 int hiLong=(int)longitude;
    int lowLong=(int)((longitude-hiLong)*multiplier);
    double newLong=lowLong/multiplier+hiLong;
 return (bNorth?"N":"S")+newLat+infill+
  (bWest?"W":"E")+newLong;
}




/* legacy java test harness
  public static void main(string[] args)
 {
    string message;
    if(args.length!=3)
     {
        message="need a grid reference like ST 767 870";
        }
    else
     {
        LongRef north=new LongRef();
        LongRef east=new LongRef();
        bool b=ConvertOSGBSquareToRefCoords(args[0],east,north);
        double easting=Double.valueOf(args[1]).doubleValue();
        double northing=Double.valueOf(args[2]).doubleValue();
        DoubleRef rlatitude=new DoubleRef ();
        DoubleRef rlongitude=new DoubleRef ();
        OSGBtoLL(easting,northing,args[0],rlatitude,rlongitude);
        double latitude=rlatitude.getValue();
        double longitude=rlongitude.getValue();

        bool bNorth=latitude>=0;
        bool bWest=longitude<=0;

  message="Latitude & Longitude ="+latitude+" , " + longitude;
        message+="\r\n or "+GetSensibleLatLongstring(latitude,
             longitude,
                            3,
                            " ");
  string square=new string();
  square=LLtoOSGB(latitude,longitude,north,east);
  message+="\r\n Grid ="+square+" "+east+" "+north;
//       message="evaluation failure on "+args[0];
        }
        System.out.print(message);
    }
*/


}; //class
};//namespace
于 2010-11-21T01:35:41.923 回答
0

我们使用proj.4库进行 WGS84 <-> OSGB36 <-> OSGBGRID 坐标变换,它工作得很好。但是我们使用 C++,所以我不知道你是否可以让它在 VB.NET 下工作。可能有包装纸之类的?(在上面的链接中提到了 PROJ.4 VB Wrappers)。

于 2009-03-20T22:10:03.477 回答