498 lines
12 KiB
C++
498 lines
12 KiB
C++
|
|
/*
|
||
|
|
----
|
||
|
|
This file is part of SECONDO.
|
||
|
|
|
||
|
|
Copyright (C) 2004, University in Hagen, Department of Computer Science,
|
||
|
|
Database Systems for New Applications.
|
||
|
|
|
||
|
|
SECONDO 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.
|
||
|
|
|
||
|
|
SECONDO 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 SECONDO; if not, write to the Free Software
|
||
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
|
----
|
||
|
|
|
||
|
|
//paragraph [1] Title: [{\Large \bf \begin {center}] [\end {center}}]
|
||
|
|
//[TOC] [\tableofcontents]
|
||
|
|
//[_] [\_]
|
||
|
|
|
||
|
|
[1] Implementation of Class Geoid
|
||
|
|
|
||
|
|
March 2011 Christian Duentgen: First implementation
|
||
|
|
|
||
|
|
|
||
|
|
1 Overview
|
||
|
|
|
||
|
|
The ~Geoid~ class represents geoids used for spherical geometry.
|
||
|
|
|
||
|
|
2 Defines and Includes
|
||
|
|
|
||
|
|
*/
|
||
|
|
|
||
|
|
|
||
|
|
#include <string>
|
||
|
|
#include <iostream>
|
||
|
|
#include <cstring>
|
||
|
|
|
||
|
|
#include "NestedList.h"
|
||
|
|
#include "ListUtils.h"
|
||
|
|
|
||
|
|
#include "Attribute.h"
|
||
|
|
#include "StandardTypes.h"
|
||
|
|
#include "Geoid.h"
|
||
|
|
|
||
|
|
#include "SecMath.h"
|
||
|
|
|
||
|
|
|
||
|
|
using namespace std;
|
||
|
|
/*
|
||
|
|
3 Class Definition
|
||
|
|
|
||
|
|
Standard constructor does nothing, because the class inherits from ~Attribute~!
|
||
|
|
|
||
|
|
*/
|
||
|
|
Geoid::Geoid() {}
|
||
|
|
|
||
|
|
Geoid::Geoid(bool _defined): Attribute(_defined), radius(6378137.000),
|
||
|
|
flattening(1.0/298.257223563){
|
||
|
|
strcpy(name, string("WGS1984").c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Constructor for arbitrary geoids. ~radius~ (positive) and ~flattening~
|
||
|
|
(non-negative). Otherwise, the an UNDEF geoid is returned!
|
||
|
|
|
||
|
|
*/
|
||
|
|
Geoid::Geoid(const string& _name,
|
||
|
|
const double _radius,
|
||
|
|
const double _flattening) : Attribute(true), radius(_radius),
|
||
|
|
flattening(_flattening)
|
||
|
|
{
|
||
|
|
if( (_flattening >= 0) // non-negative flattening
|
||
|
|
&& (_radius>0) // positive radius
|
||
|
|
&& ( _flattening <= 1.0) // equatorial r >= axial r
|
||
|
|
&& (_name.length()<=MAX_STRINGSIZE)) {
|
||
|
|
strcpy(name, _name.c_str());
|
||
|
|
} else { // use UNDEFINED as standard geoid
|
||
|
|
strcpy(name, string("UNDEFINED").c_str());
|
||
|
|
radius = 1.0;
|
||
|
|
flattening = 0.0;
|
||
|
|
SetDefined(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Constructor for special predefined Geoids.
|
||
|
|
Unknown ~GeoidName~ results in an UNDEFINED Geoid!
|
||
|
|
|
||
|
|
*/
|
||
|
|
Geoid::Geoid(const GeoidName n) : Attribute(true) {
|
||
|
|
setPredefinedGeoid(n);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Copy constructor
|
||
|
|
|
||
|
|
*/
|
||
|
|
Geoid::Geoid(const Geoid& g): Attribute(true), radius(g.radius),
|
||
|
|
flattening(g.flattening){
|
||
|
|
strcpy(name,g.name);
|
||
|
|
}
|
||
|
|
|
||
|
|
Geoid::~Geoid(){}
|
||
|
|
|
||
|
|
Geoid& Geoid::operator=(const Geoid& g) {
|
||
|
|
SetDefined(g.IsDefined());
|
||
|
|
strcpy(name, g.name);
|
||
|
|
radius = g.radius;
|
||
|
|
flattening = g.flattening;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Set the Geoid to a predined set of data.
|
||
|
|
|
||
|
|
*/
|
||
|
|
void Geoid::setPredefinedGeoid(const GeoidName n){
|
||
|
|
SetDefined(true);
|
||
|
|
switch(n)
|
||
|
|
{
|
||
|
|
case UnitSphere:
|
||
|
|
{ strcpy(name,string("UnitSphere").c_str());
|
||
|
|
radius = 1.0;
|
||
|
|
flattening = 0.0;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case WGS1984:
|
||
|
|
{ strcpy(name,string("WGS1984").c_str());
|
||
|
|
radius = 6378137.000;
|
||
|
|
flattening = 1.0/298.257223563;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case Bessel1841:
|
||
|
|
{ strcpy(name,string("Bessel1841").c_str());
|
||
|
|
radius = 6377397.155;
|
||
|
|
flattening = 1.0/299.1528434;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case Krasovsky1940:
|
||
|
|
{ strcpy(name,string("Krasovsky1940").c_str());
|
||
|
|
radius = 6378245.0;
|
||
|
|
flattening = 1.0/298.3;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case International1924 :
|
||
|
|
{ strcpy(name,string("International1924").c_str());
|
||
|
|
radius = 6378388.0;
|
||
|
|
flattening = 1.0/297.0;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case GRS1980 :
|
||
|
|
{ strcpy(name,string("GRS1980").c_str());
|
||
|
|
radius = 6378137;
|
||
|
|
flattening = 1.0/298.257222101;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
default :
|
||
|
|
{
|
||
|
|
strcpy(name,string("UNDEFINED").c_str());
|
||
|
|
radius = 1.0;
|
||
|
|
flattening = 0.0;
|
||
|
|
SetDefined(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
Function returns a string with the names of all pre-defined geoids. Can be used
|
||
|
|
in operator specs.
|
||
|
|
|
||
|
|
*/
|
||
|
|
string Geoid::getGeoIdNames(){
|
||
|
|
return "{UnitSphere, WGS1984, Bessel1841, Krasovsky1940, International1924, "
|
||
|
|
"GRS1980}";
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Get the ~GeoidName~ code for a given geoid name. If the name is unknown,
|
||
|
|
UnitSphere is returned and return parameter ~valid~ is set to ~false~.
|
||
|
|
|
||
|
|
*/
|
||
|
|
Geoid::GeoidName Geoid::getGeoIdNameFromString(const string& s, bool& valid){
|
||
|
|
valid = true;
|
||
|
|
if( s == "UnitSphere"){
|
||
|
|
return UnitSphere;
|
||
|
|
}
|
||
|
|
if (s == "WGS1984" ){
|
||
|
|
return WGS1984;
|
||
|
|
}
|
||
|
|
if (s == "Bessel1841" ){
|
||
|
|
return Bessel1841;
|
||
|
|
}
|
||
|
|
if (s == "Krasovsky1940" ){
|
||
|
|
return Krasovsky1940;
|
||
|
|
}
|
||
|
|
if (s == "International1924" ){
|
||
|
|
return International1924;
|
||
|
|
}
|
||
|
|
if (s == "GRS1980" ){
|
||
|
|
return GRS1980;
|
||
|
|
}
|
||
|
|
valid = false;
|
||
|
|
return UnitSphere;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Functions overwriting Attribute functions
|
||
|
|
|
||
|
|
*/
|
||
|
|
size_t Geoid::Sizeof() const {return sizeof(*this);}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Compare function.
|
||
|
|
A g1 greater g2 if g1's radius is larger OR the radius is equal but the flattening is
|
||
|
|
smaller OR both are equal, but the name is lexicographically larger:
|
||
|
|
|
||
|
|
*/
|
||
|
|
int Geoid::Compare( const Geoid *rhs ) const {
|
||
|
|
if(!IsDefined() && !rhs->IsDefined()){ return 0; }
|
||
|
|
if(!IsDefined() && rhs->IsDefined()){ return -1; }
|
||
|
|
if(IsDefined() && !rhs->IsDefined()){ return 1; }
|
||
|
|
if(radius > rhs->radius) { return 1; }
|
||
|
|
if(radius < rhs->radius) { return -1; }
|
||
|
|
if(flattening < rhs->flattening) { return 1; }
|
||
|
|
if(flattening > rhs->flattening) { return -1; }
|
||
|
|
int cmp = strcmp(name, rhs->name);
|
||
|
|
if(cmp < -1) { return -1; }
|
||
|
|
if(cmp > 1) { return 1; }
|
||
|
|
return cmp;
|
||
|
|
}
|
||
|
|
|
||
|
|
int Geoid::Compare( const Attribute *rhs ) const {
|
||
|
|
return Compare((Geoid*)rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Operators for GenericCompare
|
||
|
|
|
||
|
|
*/
|
||
|
|
bool Geoid::operator==(const Geoid& other) const {
|
||
|
|
return (Compare(&other) == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Geoid::operator!=(const Geoid& other) const {
|
||
|
|
return (Compare(&other) != 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Geoid::operator<(const Geoid& other) const {
|
||
|
|
return (Compare(&other) < 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Geoid::operator<=(const Geoid& other) const {
|
||
|
|
return (Compare(&other) <= 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Geoid::operator>(const Geoid& other) const {
|
||
|
|
return (Compare(&other) > 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Geoid::operator>=(const Geoid& other) const {
|
||
|
|
return (Compare(&other) >= 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Instances are NEVER adjacent:
|
||
|
|
|
||
|
|
*/
|
||
|
|
bool Geoid::Adjacent( const Attribute *attrib ) const { return false; }
|
||
|
|
|
||
|
|
/*
|
||
|
|
Print function
|
||
|
|
|
||
|
|
*/
|
||
|
|
inline ostream& Geoid::Print( ostream& os ) const {
|
||
|
|
if(IsDefined()){
|
||
|
|
return os << "(Geoid: Name=" << name << ", radius=" << radius
|
||
|
|
<< ", flattening=" << flattening << ")";
|
||
|
|
} else {
|
||
|
|
return os << "(Geoid: UNDEFINED)";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Clone function
|
||
|
|
|
||
|
|
*/
|
||
|
|
Geoid* Geoid::Clone() const {
|
||
|
|
Geoid* res = new Geoid(false);
|
||
|
|
res->SetDefined(this->IsDefined());
|
||
|
|
if(IsDefined()){
|
||
|
|
strcpy(res->name, name);
|
||
|
|
res->radius = radius;
|
||
|
|
res->flattening = flattening;
|
||
|
|
}
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Hash function
|
||
|
|
|
||
|
|
*/
|
||
|
|
size_t Geoid::HashValue() const {
|
||
|
|
if(!IsDefined()) {
|
||
|
|
return (size_t) (0);
|
||
|
|
}
|
||
|
|
unsigned long h = 0;
|
||
|
|
char* s = (char*)&radius;
|
||
|
|
for(unsigned int i = 1; i <= sizeof(double) / sizeof(char); i++)
|
||
|
|
{
|
||
|
|
h = 5 * h + *s;
|
||
|
|
s++;
|
||
|
|
}
|
||
|
|
return (size_t)(h);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Geoid::CopyFrom(const Attribute* arg) {
|
||
|
|
*this = *((Geoid*)(arg));
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Functions required for using ~GenericTC.h~
|
||
|
|
|
||
|
|
*/
|
||
|
|
ListExpr Geoid::ToListExpr(const ListExpr typeInfo ) const {
|
||
|
|
if(!IsDefined()) {
|
||
|
|
return nl->SymbolAtom("UNDEF");
|
||
|
|
}
|
||
|
|
return nl->ThreeElemList(nl->StringAtom(string(name)),
|
||
|
|
nl->RealAtom(radius),
|
||
|
|
nl->RealAtom(flattening));
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Geoid::ReadFrom(const ListExpr instance, const ListExpr typeInfo) {
|
||
|
|
SetDefined(false);
|
||
|
|
strcpy(name,string("UNDEFINED").c_str());
|
||
|
|
radius = 1.0;
|
||
|
|
flattening = 0.0;
|
||
|
|
if( nl->IsEqual(instance,"undef")
|
||
|
|
|| nl->IsEqual(instance,"UNDEF")
|
||
|
|
|| nl->IsEqual(instance,"undefined")
|
||
|
|
|| nl->IsEqual(instance,"UNDEFINED") ){
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
string geoidnameStr = "";
|
||
|
|
if(nl->IsAtom(instance)){ // single symbolAtom: predefined Geoid
|
||
|
|
if(nl->AtomType(instance)==StringType) {
|
||
|
|
geoidnameStr = nl->StringValue(instance);
|
||
|
|
} else if(nl->AtomType(instance)==SymbolType){
|
||
|
|
geoidnameStr = nl->SymbolValue(instance);
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
bool valid = false;
|
||
|
|
GeoidName geoidname = getGeoIdNameFromString(geoidnameStr, valid);
|
||
|
|
if(valid){
|
||
|
|
setPredefinedGeoid(geoidname);
|
||
|
|
} else {
|
||
|
|
SetDefined(false);
|
||
|
|
}
|
||
|
|
return valid;
|
||
|
|
}
|
||
|
|
if(nl->ListLength(instance)==3){ // user defined geoid
|
||
|
|
string _name = "UNDEFINED";
|
||
|
|
double _radius = 1.0;
|
||
|
|
double _flattening = 0.0;
|
||
|
|
// 1. element: name (accepts StringAtom or SymbolAtom)
|
||
|
|
ListExpr first = nl->First(instance);
|
||
|
|
if(!nl->IsAtom(first)) { return false; }
|
||
|
|
if(listutils::isSymbol(first)){
|
||
|
|
_name = nl->SymbolValue(first);
|
||
|
|
} else if(nl->IsAtom(first) && (nl->AtomType(first)==StringType)){
|
||
|
|
_name = nl->StringValue(first);
|
||
|
|
} else { // error
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
// 2. element: radius (accepts IntAtom or RealAtom)
|
||
|
|
ListExpr second = nl->Second(instance);
|
||
|
|
if(!listutils::isNumeric(second)){
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
_radius = listutils::getNumValue(second);
|
||
|
|
// 3. element: inverse flattening (accepts IntAtom or RealAtom)
|
||
|
|
ListExpr third = nl->Third(instance);
|
||
|
|
if(!listutils::isNumeric(third)){
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
_flattening = listutils::getNumValue(third);
|
||
|
|
// check values
|
||
|
|
if((_radius <= 0.0) || (_flattening < 0.0)){
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
SetDefined(true);
|
||
|
|
strcpy(name, _name.c_str());
|
||
|
|
radius = _radius;
|
||
|
|
flattening = _flattening;
|
||
|
|
return true;
|
||
|
|
} // else: list has wrong length -> error
|
||
|
|
SetDefined(false);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
4 Overloaded output operator
|
||
|
|
|
||
|
|
Implementation of stream output operator for ~Geoid~ objects
|
||
|
|
|
||
|
|
*/
|
||
|
|
ostream& operator<<( ostream& o, const Geoid& g ){
|
||
|
|
ios_base::fmtflags oldOptions = o.flags();
|
||
|
|
o.setf(ios_base::fixed,ios_base::floatfield);
|
||
|
|
o.precision(16);
|
||
|
|
g.Print(o);
|
||
|
|
o.flags(oldOptions);
|
||
|
|
return o;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Geoid::checkGeographicCoord(const double x, const double y){
|
||
|
|
return (x >= -180) && (x <= 180) // x <-> Longitude (Laenge)
|
||
|
|
&& (y >= -90 ) && (y <= 90);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
double Geoid::DistanceOrthodrome(double x1, double y1,
|
||
|
|
double x2, double y2,
|
||
|
|
bool& valid) const {
|
||
|
|
|
||
|
|
valid = checkGeographicCoord(x1,y1) && checkGeographicCoord(x2,y2);
|
||
|
|
if(!valid){
|
||
|
|
return 0.0;
|
||
|
|
}
|
||
|
|
double a = getR(); // sphere's equatorical radius (from geoid)
|
||
|
|
double f = getF(); // sphere's flattening (from geoid)
|
||
|
|
|
||
|
|
double b1 = y1;
|
||
|
|
double l1 = x1;
|
||
|
|
double b2 = y2;
|
||
|
|
double l2 = x2;
|
||
|
|
|
||
|
|
if(AlmostEqual(x1,x2) && AlmostEqual(y1,y2)){
|
||
|
|
return 0.0;
|
||
|
|
}
|
||
|
|
// convert coordinates from degrees to radiant
|
||
|
|
|
||
|
|
double F = (b1 + b2) * M_PI / 360.0;
|
||
|
|
double G = (b1 - b2) * M_PI / 360.0;
|
||
|
|
double l = (l1 - l2) * M_PI / 360.0;
|
||
|
|
|
||
|
|
// compute approximate distance
|
||
|
|
double coslsq = cos(l) * cos(l);
|
||
|
|
double sinlsq = sin(l) * sin(l);
|
||
|
|
double sinG = sin(G);
|
||
|
|
double cosG = cos(G);
|
||
|
|
double cosF = cos(F);
|
||
|
|
double sinF = sin(F);
|
||
|
|
double sinGsq = sinG*sinG;
|
||
|
|
double cosGsq = cosG*cosG;
|
||
|
|
double sinFsq = sinF*sinF;
|
||
|
|
double cosFsq = cosF*cosF;
|
||
|
|
|
||
|
|
double S = sinGsq*coslsq + cosFsq*sinlsq;
|
||
|
|
double C = cosGsq*coslsq + sinFsq*sinlsq;
|
||
|
|
|
||
|
|
errno = 0;
|
||
|
|
double SoverC = S/C;
|
||
|
|
assert( errno == 0 );
|
||
|
|
assert( SoverC >= 0 );
|
||
|
|
double w = atan(sqrt(SoverC));
|
||
|
|
assert( errno == 0 );
|
||
|
|
double D = 2*w*a;
|
||
|
|
|
||
|
|
// correct the distance
|
||
|
|
double R = sqrt(S*C)/w;
|
||
|
|
assert( errno == 0 );
|
||
|
|
double H1 = (3*R-1)/(2*C);
|
||
|
|
assert( errno == 0 );
|
||
|
|
double H2 = (3*R+1)/(2*S);
|
||
|
|
assert( errno == 0 );
|
||
|
|
double s = D*( 1 + f*H1*sinFsq*cosGsq - f*H2*cosFsq*sinGsq );
|
||
|
|
assert( errno == 0 );
|
||
|
|
assert( s >= 0 );
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|