Initial revision.
This commit is contained in:
parent
f6e553a1b6
commit
522bff9911
8 changed files with 997 additions and 0 deletions
11
src/BuildTiles/Osgb36/Makefile.am
Normal file
11
src/BuildTiles/Osgb36/Makefile.am
Normal file
|
@ -0,0 +1,11 @@
|
|||
noinst_LIBRARIES = libOsgb36.a
|
||||
|
||||
libOsgb36_a_SOURCES = osgb36.cxx osgb36.hxx osgbtc.cxx osgbtc.hxx uk.cxx uk.hxx
|
||||
|
||||
noinst_PROGRAMS = testosgb36
|
||||
|
||||
testosgb36_SOURCES = testosgb36.cxx
|
||||
|
||||
INCLUDES += \
|
||||
-I$(top_srcdir) \
|
||||
-I$(top_srcdir)/src/Lib
|
511
src/BuildTiles/Osgb36/osgb36.cxx
Normal file
511
src/BuildTiles/Osgb36/osgb36.cxx
Normal file
|
@ -0,0 +1,511 @@
|
|||
// osgb36.cxx -- Routines to convert UK OS coordinates to and
|
||||
// from world WGS84 coordinates.
|
||||
//
|
||||
// Written by David Luff, started December 2000.
|
||||
//
|
||||
// Copyright (C) 2000 David C. Luff - david.luff@nottingham.ac.uk
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
#include "osgb36.hxx"
|
||||
|
||||
double DEG_TO_RAD = 2.0 * 3.14159265358979323846264338327950288419716939967511 / 360.0;
|
||||
double RAD_TO_DEG = 360.0 / (2.0 * 3.14159265358979323846264338327950288419716939967511);
|
||||
|
||||
//Convert OSGB36 Lat and Lon to OSGB36 Eastings and Northings (Grid Reference)
|
||||
Point3D ConvertLatLonToEastingsNorthings(Point3D LatLon)
|
||||
{
|
||||
// ellipsoid constants
|
||||
double a; //semi-major axis (m)
|
||||
double b; //semi-minor axis (m)
|
||||
double e_squared; //ellipsoid squared eccentricity constant
|
||||
|
||||
// Airy 1830
|
||||
a = 6377563.396;
|
||||
b = 6356256.910;
|
||||
|
||||
// GRS80 (aka WGS84)
|
||||
// a = 6378137.000;
|
||||
// b = 6356752.3141;
|
||||
|
||||
e_squared = ((a*a)-(b*b))/(a*a);
|
||||
|
||||
// Projection constants
|
||||
double N_zero; //northing of true origin (m)
|
||||
double E_zero; //easting of true origin (m)
|
||||
double F_zero; //scale factor on central meridian
|
||||
double lat_zero; //latitude of true origin (rad)
|
||||
double lon_zero; //longitude of true origin and central meridian (rad)
|
||||
|
||||
// OSGB36 (UK National Grid)
|
||||
N_zero = -100000;
|
||||
E_zero = 400000;
|
||||
F_zero = 0.9996012717;
|
||||
lat_zero = 49 * DEG_TO_RAD;
|
||||
lon_zero = -2 * DEG_TO_RAD;
|
||||
|
||||
double n;
|
||||
double v;
|
||||
double rho;
|
||||
double neta_squared;
|
||||
double M;
|
||||
double I;
|
||||
double II;
|
||||
double III;
|
||||
double IIIA;
|
||||
double IV;
|
||||
double V;
|
||||
double VI;
|
||||
|
||||
double lon = LatLon.lon() * DEG_TO_RAD;
|
||||
double lat = LatLon.lat() * DEG_TO_RAD;
|
||||
|
||||
n = (a-b)/(a+b);
|
||||
v = a*F_zero*pow( (1-e_squared*sin(lat)*sin(lat)) , -0.5 );
|
||||
rho = a*F_zero*(1-e_squared)*pow((1-e_squared*sin(lat)*sin(lat)) , -1.5 );
|
||||
neta_squared = v/rho - 1.0;
|
||||
|
||||
// terms in the M equation to make it more manageable (eqn A11)
|
||||
double term1;
|
||||
double term2;
|
||||
double term3;
|
||||
double term4;
|
||||
|
||||
term1 = (1.0 + n + (5.0/4.0)*n*n + (5.0/4.0)*n*n*n)*(lat-lat_zero);
|
||||
term2 = ((3.0*n) + (3.0*n*n) + ((21.0/8.0)*n*n*n))*(sin(lat-lat_zero))*(cos(lat+lat_zero));
|
||||
term3 = (((15.0/8.0)*n*n) + ((15.0/8.0)*n*n*n))*(sin(2.0*(lat-lat_zero)))*(cos(2.0*(lat+lat_zero)));
|
||||
term4 = ((35.0/24.0)*n*n*n)*(sin(3.0*(lat-lat_zero)))*(cos(3.0*(lat+lat_zero)));
|
||||
|
||||
M = b * F_zero * (term1 - term2 + term3 - term4); //Eqn A11
|
||||
|
||||
I = M + N_zero;
|
||||
II = (v/2.0)*sin(lat)*cos(lat);
|
||||
III = (v/24.0)*sin(lat)*pow(cos(lat),3)*(5.0 - pow(tan(lat),2) + 9.0*neta_squared);
|
||||
IIIA = (v/720.0)*sin(lat)*pow(cos(lat),5)*(61.0 - 58.0*pow(tan(lat),2) + pow(tan(lat),4));
|
||||
IV = v*cos(lat);
|
||||
V = (v/6.0)*pow(cos(lat),3)*(v/rho - pow(tan(lat),2));
|
||||
VI = (v/120.0)*pow(cos(lat),5)*(5.0 - 18.0*pow(tan(lat),2) + pow(tan(lat),4) + 14.0*neta_squared - 58.0*pow(tan(lat),2)*neta_squared);
|
||||
|
||||
double N; //calculated Northing
|
||||
double E; //calculated Easting
|
||||
|
||||
N = I + II*(lon-lon_zero)*(lon-lon_zero) + III*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero) + IIIA*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero); //eqn A12
|
||||
E = E_zero + IV*(lon-lon_zero) + V*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero) + VI*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero)*(lon-lon_zero); //eqn A13
|
||||
|
||||
Point3D OSref;
|
||||
|
||||
OSref.setx(E);
|
||||
OSref.sety(N);
|
||||
OSref.setz(0.0); // Not used at present but could be used to return ODN height in future
|
||||
|
||||
return(OSref);
|
||||
}
|
||||
|
||||
|
||||
Point3D ConvertEastingsNorthingsToLatLon(Point3D GridRef)
|
||||
{
|
||||
// ellipsoid constants
|
||||
double a; //semi-major axis (m)
|
||||
double b; //semi-minor axis (m)
|
||||
double e_squared; //ellipsoid squared eccentricity constant
|
||||
|
||||
// Airy 1830
|
||||
a = 6377563.396;
|
||||
b = 6356256.910;
|
||||
|
||||
// GRS80 (aka WGS84)
|
||||
// a = 6378137.000;
|
||||
// b = 6356752.3141;
|
||||
|
||||
e_squared = ((a*a)-(b*b))/(a*a);
|
||||
|
||||
// Projection constants
|
||||
double N_zero; //northing of true origin (m)
|
||||
double E_zero; //easting of true origin (m)
|
||||
double F_zero; //scale factor on central meridian
|
||||
double lat_zero; //latitude of true origin (rad)
|
||||
double lon_zero; //longitude of true origin and central meridian (rad)
|
||||
|
||||
// OSGB36 (UK National Grid)
|
||||
N_zero = -100000;
|
||||
E_zero = 400000;
|
||||
F_zero = 0.9996012717;
|
||||
lat_zero = 49 * DEG_TO_RAD;
|
||||
lon_zero = -2 * DEG_TO_RAD;
|
||||
|
||||
double n;
|
||||
double M;
|
||||
|
||||
n = (a-b)/(a+b);
|
||||
|
||||
double E = GridRef.x();
|
||||
double N = GridRef.y();
|
||||
|
||||
// terms in the M equation to make it more manageable (eqn A11)
|
||||
double term1;
|
||||
double term2;
|
||||
double term3;
|
||||
double term4;
|
||||
|
||||
double lat_prime;
|
||||
|
||||
//calculate initial lat_prime
|
||||
lat_prime = ((N-N_zero)/(a*F_zero)) + lat_zero;
|
||||
|
||||
int i;
|
||||
|
||||
for(i=0;i<100;i++)
|
||||
{
|
||||
|
||||
|
||||
|
||||
term1 = (1.0 + n + ((5.0/4.0)*n*n) + ((5.0/4.0)*n*n*n))*(lat_prime-lat_zero);
|
||||
term2 = ((3.0*n) + (3.0*n*n) + ((21.0/8.0)*n*n*n))*(sin(lat_prime-lat_zero))*(cos(lat_prime+lat_zero));
|
||||
term3 = (((15.0/8.0)*n*n) + ((15.0/8.0)*n*n*n))*(sin(2.0*(lat_prime-lat_zero)))*(cos(2.0*(lat_prime+lat_zero)));
|
||||
term4 = ((35.0/24.0)*n*n*n)*(sin(3.0*(lat_prime-lat_zero)))*(cos(3.0*(lat_prime+lat_zero)));
|
||||
|
||||
M = b * F_zero * (term1 - term2 + term3 - term4); //Eqn A11
|
||||
|
||||
if((N - N_zero - M) < 0.001)
|
||||
break; //N - N_zero - M is less than 1mm
|
||||
else //recompute lat_prime and keep iterating until it is
|
||||
lat_prime += ((N - N_zero - M)/(a*F_zero));
|
||||
|
||||
if(i == 99)
|
||||
cout << "WARNING: Iteration failed to converge in ConvertEastingsNorthingsToLatLon\n";
|
||||
}
|
||||
|
||||
double v;
|
||||
double rho;
|
||||
double neta_squared;
|
||||
double VII;
|
||||
double VIII;
|
||||
double IX;
|
||||
double X;
|
||||
double XI;
|
||||
double XII;
|
||||
double XIIA;
|
||||
|
||||
v = a*F_zero*pow( (1-e_squared*sin(lat_prime)*sin(lat_prime)) , -0.5 );
|
||||
rho = a*F_zero*(1-e_squared)*pow((1-e_squared*sin(lat_prime)*sin(lat_prime)) , -1.5 );
|
||||
neta_squared = v/rho - 1.0;
|
||||
|
||||
VII = tan(lat_prime) / (2.0*rho*v);
|
||||
VIII = (tan(lat_prime)/(24.0*rho*v*v*v)) * (5.0 + 3.0*pow(tan(lat_prime),2.0) + neta_squared - 9.0*pow(tan(lat_prime),2.0)*neta_squared);
|
||||
IX = (tan(lat_prime)/(720.0*rho*v*v*v*v*v)) * (61.0 + 90.0*pow(tan(lat_prime),2.0) + 45.0*pow(tan(lat_prime),4.0));
|
||||
X = (1/cos(lat_prime)) / v;
|
||||
XI = ((1/cos(lat_prime)) / (6.0 * pow(v,3))) * (v/rho + 2*pow(tan(lat_prime),2));
|
||||
XII = ((1/cos(lat_prime)) / (120.0 * pow(v,5))) * (5.0 + 28.0*pow(tan(lat_prime),2) + 24.0*pow(tan(lat_prime),4));
|
||||
XIIA = ((1/cos(lat_prime)) / (5040.0 * pow(v,7))) * (61.0 + 662.0*pow(tan(lat_prime),2) + 1320.0*pow(tan(lat_prime),4) + 720.0*pow(tan(lat_prime),6));
|
||||
|
||||
double lat;
|
||||
double lon;
|
||||
|
||||
lat = lat_prime - VII*pow((E-E_zero),2) + VIII*pow((E-E_zero),4) + IX*pow((E-E_zero),6);
|
||||
lon = lon_zero + X*(E-E_zero) - XI*pow((E-E_zero),3) + XII*pow((E-E_zero),5) - XIIA*pow((E-E_zero),7);
|
||||
|
||||
Point3D LatLon;
|
||||
LatLon.setlon(lon * RAD_TO_DEG);
|
||||
LatLon.setlat(lat * RAD_TO_DEG);
|
||||
LatLon.setelev(0.0);
|
||||
|
||||
return(LatLon);
|
||||
}
|
||||
|
||||
|
||||
Point3D ConvertAiry1830PolarToCartesian(Point3D LatLon)
|
||||
{
|
||||
|
||||
double lon = LatLon.lon() * DEG_TO_RAD;
|
||||
double lat = LatLon.lat() * DEG_TO_RAD;
|
||||
double H = LatLon.elev() * DEG_TO_RAD;
|
||||
|
||||
// ellipsoid constants
|
||||
double a; //semi-major axis (m)
|
||||
double b; //semi-minor axis (m)
|
||||
double e_squared; //ellipsoid squared eccentricity constant
|
||||
|
||||
// Airy 1830
|
||||
a = 6377563.396;
|
||||
b = 6356256.910;
|
||||
|
||||
e_squared = ((a*a)-(b*b))/(a*a);
|
||||
|
||||
double v;
|
||||
|
||||
v = a*pow( (1-e_squared*sin(lat)*sin(lat)) , -0.5 );
|
||||
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
Point3D Cartesian;
|
||||
|
||||
x = (v+H)*cos(lat)*cos(lon);
|
||||
y = (v+H)*cos(lat)*sin(lon);
|
||||
z = ((1-e_squared)*v + H)*sin(lat);
|
||||
|
||||
Cartesian.setx(x);
|
||||
Cartesian.sety(y);
|
||||
Cartesian.setz(z);
|
||||
|
||||
return(Cartesian);
|
||||
}
|
||||
|
||||
|
||||
Point3D ConvertGRS80PolarToCartesian(Point3D LatLon)
|
||||
{
|
||||
|
||||
double lon = LatLon.lon() * DEG_TO_RAD;
|
||||
double lat = LatLon.lat() * DEG_TO_RAD;
|
||||
double H = LatLon.elev() * DEG_TO_RAD;
|
||||
|
||||
// ellipsoid constants
|
||||
double a; //semi-major axis (m)
|
||||
double b; //semi-minor axis (m)
|
||||
double e_squared; //ellipsoid squared eccentricity constant
|
||||
|
||||
|
||||
// GRS80 (aka WGS84)
|
||||
a = 6378137.000;
|
||||
b = 6356752.3141;
|
||||
|
||||
e_squared = ((a*a)-(b*b))/(a*a);
|
||||
|
||||
double v;
|
||||
|
||||
v = a*pow( (1-e_squared*sin(lat)*sin(lat)) , -0.5 );
|
||||
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
Point3D Cartesian;
|
||||
|
||||
x = (v+H)*cos(lat)*cos(lon);
|
||||
y = (v+H)*cos(lat)*sin(lon);
|
||||
z = ((1-e_squared)*v + H)*sin(lat);
|
||||
|
||||
Cartesian.setx(x);
|
||||
Cartesian.sety(y);
|
||||
Cartesian.setz(z);
|
||||
|
||||
return(Cartesian);
|
||||
}
|
||||
|
||||
|
||||
Point3D ConvertAiry1830CartesianToPolar(Point3D XYZCoord)
|
||||
{
|
||||
double x = XYZCoord.x();
|
||||
double y = XYZCoord.y();
|
||||
double z = XYZCoord.z();
|
||||
double p;
|
||||
double lat1;
|
||||
double lat2;
|
||||
double lat;
|
||||
double lon;
|
||||
double H;
|
||||
Point3D LatLon;
|
||||
|
||||
// ellipsoid constants
|
||||
double a; //semi-major axis (m)
|
||||
double b; //semi-minor axis (m)
|
||||
double e_squared; //ellipsoid squared eccentricity constant
|
||||
|
||||
// Airy 1830
|
||||
a = 6377563.396;
|
||||
b = 6356256.910;
|
||||
|
||||
e_squared = ((a*a)-(b*b))/(a*a);
|
||||
|
||||
double v;
|
||||
|
||||
lon = atan(y/x);
|
||||
|
||||
p = sqrt(x*x + y*y);
|
||||
|
||||
//lat is obtained iteratively
|
||||
//obtain initial value of lat
|
||||
lat1 = atan(z/(p*(1-e_squared)));
|
||||
|
||||
// cout << "Initial value of lat = " << lat1 << '\n';
|
||||
|
||||
int i;
|
||||
|
||||
for(i=0;i<100;i++)
|
||||
{
|
||||
v = a*pow( (1-e_squared*sin(lat1)*sin(lat1)) , -0.5 );
|
||||
|
||||
lat2 = atan((z + e_squared*v*sin(lat1))/p);
|
||||
|
||||
// cout << "lat2 = " << lat2 << '\n';
|
||||
|
||||
if(fabs(lat2-lat1) < 0.00000001)
|
||||
{
|
||||
lat = lat2;
|
||||
// cout << i << " iterations required in ConvertAiry1830CartesianToPolar\n";
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
lat1 = lat2;
|
||||
}
|
||||
|
||||
if(i == 99)
|
||||
cout << "WARNING: Iteration failed to converge in ConvertAiry1830CartesianToPolar\n";
|
||||
}
|
||||
|
||||
H = p/cos(lat) - v;
|
||||
|
||||
LatLon.setlon(lon * RAD_TO_DEG);
|
||||
LatLon.setlat(lat * RAD_TO_DEG);
|
||||
LatLon.setelev(H);
|
||||
|
||||
return(LatLon);
|
||||
}
|
||||
|
||||
|
||||
Point3D ConvertGRS80CartesianToPolar(Point3D XYZCoord)
|
||||
{
|
||||
double x = XYZCoord.x();
|
||||
double y = XYZCoord.y();
|
||||
double z = XYZCoord.z();
|
||||
double p;
|
||||
double lat1;
|
||||
double lat2;
|
||||
double lat;
|
||||
double lon;
|
||||
double H;
|
||||
Point3D LatLon;
|
||||
|
||||
// ellipsoid constants
|
||||
double a; //semi-major axis (m)
|
||||
double b; //semi-minor axis (m)
|
||||
double e_squared; //ellipsoid squared eccentricity constant
|
||||
|
||||
// GRS80 (aka WGS84)
|
||||
a = 6378137.000;
|
||||
b = 6356752.3141;
|
||||
|
||||
e_squared = ((a*a)-(b*b))/(a*a);
|
||||
|
||||
double v;
|
||||
|
||||
lon = atan(y/x);
|
||||
|
||||
p = sqrt(x*x + y*y);
|
||||
|
||||
//lat is obtained iteratively
|
||||
//obtain initial value of lat
|
||||
lat1 = atan(z/(p*(1-e_squared)));
|
||||
|
||||
int i;
|
||||
|
||||
for(i=0;i<100;i++)
|
||||
{
|
||||
v = a*pow( (1-e_squared*sin(lat1)*sin(lat1)) , -0.5 );
|
||||
|
||||
lat2 = atan((z + e_squared*v*sin(lat1))/p);
|
||||
|
||||
if(fabs(lat2-lat1) < 0.00000001)
|
||||
{
|
||||
lat = lat2;
|
||||
// cout << i << " iterations required in ConvertGRS80CartesianToPolar\n";
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
lat1 = lat2;
|
||||
}
|
||||
|
||||
if(i == 99)
|
||||
cout << "WARNING: Iteration failed to converge in ConvertGRS80CartesianToPolar\n";
|
||||
}
|
||||
|
||||
H = p/cos(lat) - v;
|
||||
|
||||
LatLon.setlon(lon * RAD_TO_DEG);
|
||||
LatLon.setlat(lat * RAD_TO_DEG);
|
||||
LatLon.setelev(H);
|
||||
|
||||
return(LatLon);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Transform a point in WGS84 cartesian coordinates to OSGB36 cartesian coordinates
|
||||
//Uses the Helmert Transformation with coefficients from www.gps.gov.org
|
||||
//Only accurate to around 5m since OSGB36 is an imhomogenous TRF
|
||||
Point3D ConvertWGS84ToOSGB36(Point3D WGS84)
|
||||
{
|
||||
Point3D OSGB36;
|
||||
|
||||
double tx = -446.448; // (m)
|
||||
double ty = 125.157;
|
||||
double tz = -542.060;
|
||||
double rx = -0.1502; // (sec)
|
||||
double ry = -0.2470;
|
||||
double rz = -0.8421;
|
||||
double s = 20.4894; // scale factor in ppm
|
||||
|
||||
// Convert rotations from seconds of arc to radians
|
||||
rx = rx / 3600.0 * DEG_TO_RAD;
|
||||
ry = ry / 3600.0 * DEG_TO_RAD;
|
||||
rz = rz / 3600.0 * DEG_TO_RAD;
|
||||
|
||||
s /= 1000000;
|
||||
|
||||
// cout << "(1+s) = " << (1+s) << '\n';
|
||||
|
||||
OSGB36.setx(tx + (1+s)*WGS84.x() - rz*WGS84.y() + ry*WGS84.z());
|
||||
OSGB36.sety(ty + rz*WGS84.x() + (1+s)*WGS84.y() - rx*WGS84.z());
|
||||
OSGB36.setz(tz - ry*WGS84.x() + rx*WGS84.y() + (1+s)*WGS84.z());
|
||||
|
||||
return(OSGB36);
|
||||
}
|
||||
|
||||
|
||||
//Reverse transformation achieved by reversing the signs of coefficients for the above.
|
||||
//Only valid for small angles, and has same accuracy limitations
|
||||
Point3D ConvertOSGB36ToWGS84(Point3D OSGB36)
|
||||
{
|
||||
Point3D WGS84;
|
||||
|
||||
double tx = 446.448; // (m)
|
||||
double ty = -125.157;
|
||||
double tz = 542.060;
|
||||
double rx = 0.1502; // (sec)
|
||||
double ry = 0.2470;
|
||||
double rz = 0.8421;
|
||||
double s = -20.4894; // scale factor in ppm
|
||||
|
||||
// Convert rotations from seconds to radians
|
||||
rx = rx / 3600.0 * DEG_TO_RAD;
|
||||
ry = ry / 3600.0 * DEG_TO_RAD;
|
||||
rz = rz / 3600.0 * DEG_TO_RAD;
|
||||
|
||||
s /= 1000000;
|
||||
|
||||
WGS84.setx(tx + (1+s)*OSGB36.x() - rz*OSGB36.y() + ry*OSGB36.z());
|
||||
WGS84.sety(ty + rz*OSGB36.x() + (1+s)*OSGB36.y() - rx*OSGB36.z());
|
||||
WGS84.setz(tz - ry*OSGB36.x() + rx*OSGB36.y() + (1+s)*OSGB36.z());
|
||||
|
||||
return(WGS84);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
64
src/BuildTiles/Osgb36/osgb36.hxx
Normal file
64
src/BuildTiles/Osgb36/osgb36.hxx
Normal file
|
@ -0,0 +1,64 @@
|
|||
// osgb36.hxx -- Routines to convert UK OS coordinates to and
|
||||
// from world WGS84 coordinates.
|
||||
//
|
||||
// Written by David Luff, started December 2000.
|
||||
//
|
||||
// Copyright (C) 2000 David C. Luff - david.luff@nottingham.ac.uk
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
#include <iostream.h>
|
||||
#include <math.h>
|
||||
#include <simgear/math/point3d.hxx>
|
||||
|
||||
|
||||
//*******************************************************************
|
||||
//
|
||||
// NOTE All functions which take or return Latitude and Longitude
|
||||
// take and return degrees. Conversion to and from radians
|
||||
// is performed internaly. XYZ coordinates and ellipsoid
|
||||
// heights are returned and required in meters.
|
||||
//
|
||||
//*******************************************************************
|
||||
|
||||
|
||||
//Convert OSGB36 Lat and Lon to OSGB36 Eastings and Northings (Grid Reference)
|
||||
Point3D ConvertLatLonToEastingsNorthings(Point3D LatLon);
|
||||
|
||||
//Convert OSGB36 Eastings and Northings to OSGB36 Lat and Lon
|
||||
Point3D ConvertEastingsNorthingsToLatLon(Point3D GridRef);
|
||||
|
||||
//Convert OSGB36 coordinates from polar to cartesian (x,y,z) form
|
||||
Point3D ConvertAiry1830PolarToCartesian(Point3D LatLon);
|
||||
|
||||
//Convert WGS84 coordinates from polar to cartesian (x,y,z) form
|
||||
Point3D ConvertGRS80PolarToCartesian(Point3D LatLon);
|
||||
|
||||
//Convert OSGB36 coordinates from cartesian (x,y,z) to polar form
|
||||
Point3D ConvertAiry1830CartesianToPolar(Point3D XYZCoord);
|
||||
|
||||
//Convert WGS84 coordinates from cartesian (x,y,z) to polar form
|
||||
Point3D ConvertGRS80CartesianToPolar(Point3D XYZCoord);
|
||||
|
||||
//Transform a point in WGS84 cartesian coordinates to OSGB36 cartesian coordinates
|
||||
//Uses the Helmert Transformation with coefficients from www.gps.gov.org
|
||||
//Only accurate to around 5m since OSGB36 is an inhomogenous TRF
|
||||
Point3D ConvertWGS84ToOSGB36(Point3D WGS84);
|
||||
|
||||
//Transform a point in OSGB36 cartesian coordinates to WGS84 cartesian coordinates
|
||||
//Uses the Helmert Transformation with coefficients from www.gps.gov.org
|
||||
//Only accurate to around 5m since OSGB36 is an inhomogenous TRF
|
||||
Point3D ConvertOSGB36ToWGS84(Point3D OSGB36);
|
||||
|
220
src/BuildTiles/Osgb36/osgbtc.cxx
Normal file
220
src/BuildTiles/Osgb36/osgbtc.cxx
Normal file
|
@ -0,0 +1,220 @@
|
|||
#include "osgb36.hxx"
|
||||
#include "uk.hxx"
|
||||
|
||||
#include "osgbtc.hxx"
|
||||
|
||||
|
||||
// traverse the specified fan/strip/list of vertices and attempt to
|
||||
// calculate "none stretching" texture coordinates
|
||||
point_list UK_calc_tex_coords( const SGBucket& b, const point_list& geod_nodes,
|
||||
const int_list& fan, double scale )
|
||||
{
|
||||
// cout << "calculating texture coordinates for a specific fan of size = "
|
||||
// << fan.size() << endl;
|
||||
|
||||
// calculate perimeter based on center of this degree (not center
|
||||
// of bucket)
|
||||
/* double clat = (int)b.get_center_lat();
|
||||
if ( clat > 0 ) {
|
||||
clat = (int)clat + 0.5;
|
||||
} else {
|
||||
clat = (int)clat - 0.5;
|
||||
}
|
||||
*/
|
||||
// double clat_rad = clat * DEG_TO_RAD;
|
||||
// double cos_lat = cos( clat_rad );
|
||||
// double local_radius = cos_lat * EQUATORIAL_RADIUS_M;
|
||||
// double local_perimeter = 2.0 * local_radius * FG_PI;
|
||||
// double degree_width = local_perimeter / 360.0;
|
||||
|
||||
// cout << "clat = " << clat << endl;
|
||||
// cout << "clat (radians) = " << clat_rad << endl;
|
||||
// cout << "cos(lat) = " << cos_lat << endl;
|
||||
// cout << "local_radius = " << local_radius << endl;
|
||||
// cout << "local_perimeter = " << local_perimeter << endl;
|
||||
// cout << "degree_width = " << degree_width << endl;
|
||||
|
||||
// double perimeter = 2.0 * EQUATORIAL_RADIUS_M * FG_PI;
|
||||
// double degree_height = perimeter / 360.0;
|
||||
// cout << "degree_height = " << degree_height << endl;
|
||||
|
||||
// find min/max of fan
|
||||
Point3D tmin, tmax, WGS84LatLon, t;
|
||||
Point3D WGS84xyz, OSGB36xyz, OSGB36LatLon;
|
||||
Point3D OSGridRef;
|
||||
bool first = true;
|
||||
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < (int)fan.size(); ++i ) {
|
||||
WGS84LatLon = geod_nodes[ fan[i] ];
|
||||
// cout << "point WGS84LatLon = " << WGS84LatLon << endl;
|
||||
WGS84xyz = ConvertGRS80PolarToCartesian(WGS84LatLon);
|
||||
// cout << "WGS84XYZ = " << WGS84XYZ.x << ", " << WGS84XYZ.y << ", " << WGS84XYZ.z << '\n';
|
||||
OSGB36xyz = ConvertWGS84ToOSGB36(WGS84xyz);
|
||||
// cout << "OSXYZ = " << OSXYZ.x << ", " << OSXYZ.y << ", " << OSXYZ.z << '\n';
|
||||
OSGB36LatLon = ConvertAiry1830CartesianToPolar(OSGB36xyz);
|
||||
// cout << "OSGB36LatLon = " << OSGB36LatLon.x() << ", " << OSGB36LatLon.y() << ", " << OSGB36LatLon.z() << '\n';
|
||||
OSGridRef = ConvertLatLonToEastingsNorthings(OSGB36LatLon);
|
||||
// cout << "OS Eastings and Northings = " << OSGridRef << '\n';
|
||||
|
||||
|
||||
|
||||
t = UK_basic_tex_coord( OSGridRef );
|
||||
// cout << "basic_tex_coord = " << t << endl;
|
||||
|
||||
// cout << "p = " << p << '\n';
|
||||
// cout << "t = " << t << '\n';
|
||||
// cout << "Degree width = " << degree_width << '\n';
|
||||
// cout << "Degree height = " << degree_height << '\n';
|
||||
// char dcl_pause;
|
||||
// cin >> dcl_pause;
|
||||
|
||||
if ( first ) {
|
||||
tmin = tmax = t;
|
||||
first = false;
|
||||
} else {
|
||||
if ( t.x() < tmin.x() ) {
|
||||
tmin.setx( t.x() );
|
||||
}
|
||||
if ( t.y() < tmin.y() ) {
|
||||
tmin.sety( t.y() );
|
||||
}
|
||||
if ( t.x() > tmax.x() ) {
|
||||
tmax.setx( t.x() );
|
||||
}
|
||||
if ( t.y() > tmax.y() ) {
|
||||
tmax.sety( t.y() );
|
||||
}
|
||||
}
|
||||
|
||||
// cout << "tmin.x = " << tmin.x() << " tmax.x = " << tmax.x() << " tmin.y = " << tmin.y() << " tmax.y = " << tmax.y() << '\n';
|
||||
}
|
||||
|
||||
double dx = fabs( tmax.x() - tmin.x() );
|
||||
double dy = fabs( tmax.y() - tmin.y() );
|
||||
// cout << "dx = " << dx << " dy = " << dy << endl;
|
||||
|
||||
bool do_shift = false;
|
||||
// Point3D mod_shift;
|
||||
if ( (dx > HALF_MAX_TEX_COORD) || (dy > HALF_MAX_TEX_COORD) ) {
|
||||
// structure is too big, we'll just have to shift it so that
|
||||
// tmin = (0,0). This messes up subsequent texture scaling,
|
||||
// but is the best we can do.
|
||||
// cout << "SHIFTING" << endl;
|
||||
do_shift = true;
|
||||
if ( tmin.x() < 0 ) {
|
||||
tmin.setx( (double)( (int)tmin.x() - 1 ) );
|
||||
} else {
|
||||
tmin.setx( (int)tmin.x() );
|
||||
}
|
||||
if ( tmin.y() < 0 ) {
|
||||
tmin.sety( (double)( (int)tmin.y() - 1 ) );
|
||||
} else {
|
||||
tmin.sety( (int)tmin.y() );
|
||||
}
|
||||
// cout << "found tmin = " << tmin << endl;
|
||||
} else {
|
||||
if ( tmin.x() < 0 ) {
|
||||
tmin.setx( ( (int)(tmin.x() / HALF_MAX_TEX_COORD) - 1 )
|
||||
* HALF_MAX_TEX_COORD );
|
||||
} else {
|
||||
tmin.setx( ( (int)(tmin.x() / HALF_MAX_TEX_COORD) )
|
||||
* HALF_MAX_TEX_COORD );
|
||||
}
|
||||
if ( tmin.y() < 0 ) {
|
||||
tmin.sety( ( (int)(tmin.y() / HALF_MAX_TEX_COORD) - 1 )
|
||||
* HALF_MAX_TEX_COORD );
|
||||
} else {
|
||||
tmin.sety( ( (int)(tmin.y() / HALF_MAX_TEX_COORD) )
|
||||
* HALF_MAX_TEX_COORD );
|
||||
}
|
||||
#if 0
|
||||
// structure is small enough ... we can mod it so we can
|
||||
// properly scale the texture coordinates later.
|
||||
// cout << "MODDING" << endl;
|
||||
double x1 = fmod(tmin.x(), MAX_TEX_COORD);
|
||||
while ( x1 < 0 ) { x1 += MAX_TEX_COORD; }
|
||||
|
||||
double y1 = fmod(tmin.y(), MAX_TEX_COORD);
|
||||
while ( y1 < 0 ) { y1 += MAX_TEX_COORD; }
|
||||
|
||||
double x2 = fmod(tmax.x(), MAX_TEX_COORD);
|
||||
while ( x2 < 0 ) { x2 += MAX_TEX_COORD; }
|
||||
|
||||
double y2 = fmod(tmax.y(), MAX_TEX_COORD);
|
||||
while ( y2 < 0 ) { y2 += MAX_TEX_COORD; }
|
||||
|
||||
// At this point we know that the object is < 16 wide in
|
||||
// texture coordinate space. If the modulo of the tmin is >
|
||||
// the mod of the tmax at this point, then we know that the
|
||||
// starting tex coordinate for the tmax > 16 so we can shift
|
||||
// everything down by 16 and get it within the 0-32 range.
|
||||
|
||||
if ( x1 > x2 ) {
|
||||
mod_shift.setx( HALF_MAX_TEX_COORD );
|
||||
} else {
|
||||
mod_shift.setx( 0.0 );
|
||||
}
|
||||
|
||||
if ( y1 > y2 ) {
|
||||
mod_shift.sety( HALF_MAX_TEX_COORD );
|
||||
} else {
|
||||
mod_shift.sety( 0.0 );
|
||||
}
|
||||
#endif
|
||||
// cout << "mod_shift = " << mod_shift << endl;
|
||||
}
|
||||
|
||||
// generate tex_list
|
||||
Point3D adjusted_t;
|
||||
point_list tex;
|
||||
tex.clear();
|
||||
for ( i = 0; i < (int)fan.size(); ++i ) {
|
||||
WGS84LatLon = geod_nodes[ fan[i] ];
|
||||
// cout << "About to do second run through tex coords\n";
|
||||
// cout << "point WGS84LatLon = " << WGS84LatLon << endl;
|
||||
WGS84xyz = ConvertGRS80PolarToCartesian(WGS84LatLon);
|
||||
// cout << "WGS84XYZ = " << WGS84XYZ.x << ", " << WGS84XYZ.y << ", " << WGS84XYZ.z << '\n';
|
||||
OSGB36xyz = ConvertWGS84ToOSGB36(WGS84xyz);
|
||||
// cout << "OSXYZ = " << OSXYZ.x << ", " << OSXYZ.y << ", " << OSXYZ.z << '\n';
|
||||
OSGB36LatLon = ConvertAiry1830CartesianToPolar(OSGB36xyz);
|
||||
// cout << "OSGB36LatLon = " << OSGB36LatLon.x() << ", " << OSGB36LatLon.y() << ", " << OSGB36LatLon.z() << '\n';
|
||||
OSGridRef = ConvertLatLonToEastingsNorthings(OSGB36LatLon);
|
||||
// cout << "OS Eastings and Northings = " << OSGridRef << '\n';
|
||||
|
||||
t = UK_basic_tex_coord(OSGridRef);
|
||||
// cout << "second t = " << t << endl;
|
||||
|
||||
// if ( do_shift ) {
|
||||
adjusted_t = t - tmin;
|
||||
// cout << "adjusted_t = " << adjusted_t << '\n';
|
||||
#if 0
|
||||
} else {
|
||||
adjusted_t.setx( fmod(t.x() + mod_shift.x(), MAX_TEX_COORD) );
|
||||
while ( adjusted_t.x() < 0 ) {
|
||||
adjusted_t.setx( adjusted_t.x() + MAX_TEX_COORD );
|
||||
}
|
||||
adjusted_t.sety( fmod(t.y() + mod_shift.y(), MAX_TEX_COORD) );
|
||||
while ( adjusted_t.y() < 0 ) {
|
||||
adjusted_t.sety( adjusted_t.y() + MAX_TEX_COORD );
|
||||
}
|
||||
// cout << "adjusted_t " << adjusted_t << endl;
|
||||
}
|
||||
#endif
|
||||
if ( adjusted_t.x() < SG_EPSILON ) {
|
||||
adjusted_t.setx( 0.0 );
|
||||
}
|
||||
if ( adjusted_t.y() < SG_EPSILON ) {
|
||||
adjusted_t.sety( 0.0 );
|
||||
}
|
||||
adjusted_t.setz( 0.0 );
|
||||
cout << "adjusted_t after check for SG_EPSILON = " << adjusted_t << endl;
|
||||
|
||||
tex.push_back( adjusted_t );
|
||||
}
|
||||
// char dcl_pause2;
|
||||
// cin >> dcl_pause2;
|
||||
|
||||
return tex;
|
||||
}
|
31
src/BuildTiles/Osgb36/osgbtc.hxx
Normal file
31
src/BuildTiles/Osgb36/osgbtc.hxx
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef OSBTEXCOORD_H
|
||||
#define OSBTEXCOORD_H
|
||||
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include <simgear/math/point3d.hxx>
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
|
||||
|
||||
#define FG_STANDARD_TEXTURE_DIMENSION 1000.0 // meters
|
||||
#define MAX_TEX_COORD 8.0
|
||||
#define HALF_MAX_TEX_COORD ( MAX_TEX_COORD / 2.0 )
|
||||
|
||||
|
||||
// return the basic unshifted/unmoded texture coordinate for a Easting/Northing (UK) grid reference
|
||||
inline Point3D UK_basic_tex_coord( const Point3D& p )
|
||||
{
|
||||
// cout << "Point in dcl_basic_tex_coord is " << p << '\n';
|
||||
return Point3D( p.x() / FG_STANDARD_TEXTURE_DIMENSION ,
|
||||
p.y() / FG_STANDARD_TEXTURE_DIMENSION ,
|
||||
0.0 );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// traverse the specified fan/strip/list of vertices and attempt to
|
||||
// calculate "none stretching" texture coordinates
|
||||
point_list UK_calc_tex_coords( const SGBucket& b, const point_list& geod_nodes,
|
||||
const int_list& fan, double scale );
|
||||
|
||||
#endif // OSBTEXCOORD_H
|
74
src/BuildTiles/Osgb36/testosgb36.cxx
Normal file
74
src/BuildTiles/Osgb36/testosgb36.cxx
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include <simgear/math/point3d.hxx>
|
||||
|
||||
#include "osgb36.hxx"
|
||||
|
||||
//Test harness for various WGS84 and OSGB36 conversion functions
|
||||
int main()
|
||||
{
|
||||
cout << "Test OSGB36\n";
|
||||
/* Point3D initial_position;
|
||||
Point3D final_position;
|
||||
|
||||
initial_position.setlat(52.0 + ((39.0 + 27.2531/60.0)/60.0));
|
||||
initial_position.setlon(1.0 + ((43.0 + 4.5177/60.0)/60.0));
|
||||
initial_position.setelev(0.0);
|
||||
|
||||
final_position = ConvertLatLonToEastingsNorthings(initial_position);
|
||||
|
||||
cout << "Eastings = " << final_position.x() << '\n';
|
||||
cout << "Northings = " << final_position.y() << '\n';
|
||||
|
||||
Point3D check_position;
|
||||
|
||||
check_position = ConvertEastingsNorthingsToLatLon(final_position);
|
||||
|
||||
cout << "Lat = " << check_position.lat() << '\n';
|
||||
cout << "Lon = " << check_position.lon() << '\n';
|
||||
cout << "Elipsiodal height = " << check_position.elev() << '\n';
|
||||
|
||||
check_position.setelev(24.7);
|
||||
|
||||
Point3D cartesian_position = ConvertAiry1830PolarToCartesian(check_position);
|
||||
|
||||
cout << "X = " << cartesian_position.x() << '\n';
|
||||
cout << "Y = " << cartesian_position.y() << '\n';
|
||||
cout << "Z = " << cartesian_position.z() << '\n';
|
||||
|
||||
cout << "\nTesting ConvertAiry1830CartesianToPolar.....\n";
|
||||
|
||||
Point3D XYZPosition;
|
||||
|
||||
XYZPosition.setx(3874938.849);
|
||||
XYZPosition.sety(116218.624);
|
||||
XYZPosition.setz(5047168.208);
|
||||
|
||||
Point3D LatLonPosition = ConvertAiry1830CartesianToPolar(XYZPosition);
|
||||
|
||||
cout << "Lat = " << LatLonPosition.lat() << '\n';
|
||||
cout << "Lon = " << LatLonPosition.lon() << '\n';
|
||||
cout << "Elipsiodal height = " << LatLonPosition.elev() << '\n';
|
||||
|
||||
cout << "\nNow attempting to convert WGS84 lat & lon to OS grid reference .....\n";
|
||||
|
||||
Point3D WGS84LatLon;
|
||||
Point3D WGS84XYZ;
|
||||
Point3D OSXYZ;
|
||||
Point3D OSLatLon;
|
||||
Point3D OSGridRef;
|
||||
|
||||
WGS84LatLon.setlat(52.0);
|
||||
WGS84LatLon.setlon(-2.0);
|
||||
WGS84LatLon.setelev(0.0);
|
||||
|
||||
WGS84XYZ = ConvertGRS80PolarToCartesian(WGS84LatLon);
|
||||
cout << "WGS84XYZ = " << WGS84XYZ.x() << ", " << WGS84XYZ.y() << ", " << WGS84XYZ.z() << '\n';
|
||||
OSXYZ = ConvertWGS84ToOSGB36(WGS84XYZ);
|
||||
cout << "OSXYZ = " << OSXYZ.x() << ", " << OSXYZ.y() << ", " << OSXYZ.z() << '\n';
|
||||
OSLatLon = ConvertAiry1830CartesianToPolar(OSXYZ);
|
||||
cout << "OSLatLon = " << OSLatLon.lat() << ", " << OSLatLon.lon() << ", " << OSLatLon.elev() << '\n';
|
||||
OSGridRef = ConvertLatLonToEastingsNorthings(OSLatLon);
|
||||
|
||||
cout << "OS Grid Reference = " << OSGridRef.x() << ", " << OSGridRef.y() << "\n\n";
|
||||
*/
|
||||
return(0);
|
||||
}
|
61
src/BuildTiles/Osgb36/uk.cxx
Normal file
61
src/BuildTiles/Osgb36/uk.cxx
Normal file
|
@ -0,0 +1,61 @@
|
|||
// uk.cxx -- Routines to determine whether a point is within the UK.
|
||||
//
|
||||
// Written by David Luff, started Janurary 2001.
|
||||
//
|
||||
// Copyright (C) 2001 David C. Luff - david.luff@nottingham.ac.uk
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
#include "uk.hxx"
|
||||
|
||||
//Returns true if a point is within the mainland UK, excluding
|
||||
//Northern Ireland. Requires lat and lon to be passed in degrees.
|
||||
bool isInUK(Point3D p)
|
||||
{
|
||||
bool inUK = false;
|
||||
double lat = p.lat();
|
||||
double lon = p.lon();
|
||||
//The entire UK (excepting a small portion of Northern Ireland which we arn't doing anyway)
|
||||
//falls within the box of -8 -> 2 deg lon, and 49 -> 60 deg lat
|
||||
//We'll check for within this box first, and if so then we'll refine
|
||||
//further to avoid bits of France and Ireland.
|
||||
if((lat >= 49.0) && (lat <= 60.0) && (lon >= -8.0) && (lon <= 2.0))
|
||||
{
|
||||
//we might be within the UK
|
||||
//set inUK = true and then test for the exceptions
|
||||
inUK = true;
|
||||
|
||||
//check for France (Normandy/Calais)
|
||||
if((lon >= -2.0) && (lat <=50.0))
|
||||
//Normandy
|
||||
inUK = false;
|
||||
if((lon > 1.0) && (lat <= 51.0))
|
||||
//Calais area
|
||||
inUK = false;
|
||||
|
||||
//Check for Ireland
|
||||
if((lon <= -6.0) && (lat > 51.0) && (lat <= 55.5))
|
||||
//Ireland
|
||||
inUK = false;
|
||||
//Check for last bit of NI
|
||||
if((lat > 54.0) && (lat <= 55.1) && (lon <= -5.3333))
|
||||
inUK = false;
|
||||
|
||||
}
|
||||
else
|
||||
inUK = false;
|
||||
|
||||
return(inUK);
|
||||
}
|
25
src/BuildTiles/Osgb36/uk.hxx
Normal file
25
src/BuildTiles/Osgb36/uk.hxx
Normal file
|
@ -0,0 +1,25 @@
|
|||
// uk.hxx -- Routines to determine whether a point is within the UK.
|
||||
//
|
||||
// Written by David Luff, started Janurary 2001.
|
||||
//
|
||||
// Copyright (C) 2001 David C. Luff - david.luff@nottingham.ac.uk
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
#include <simgear/math/point3d.hxx>
|
||||
|
||||
//Returns true if a point is within the mainland UK, excluding
|
||||
//Northern Ireland. Requires lat and lon to be passed in degrees.
|
||||
bool isInUK(Point3D p);
|
Loading…
Reference in a new issue