00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082 #include <ctype.h>
00083 #include <math.h>
00084 #include <stdio.h>
00085 #include <string.h>
00086 #include "TransverseMercator.h"
00087 #include "BritishNationalGrid.h"
00088 #include "BNGCoordinates.h"
00089 #include "EllipsoidParameters.h"
00090 #include "MapProjectionCoordinates.h"
00091 #include "GeodeticCoordinates.h"
00092 #include "CoordinateConversionException.h"
00093 #include "ErrorMessages.h"
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110 using namespace MSP::CCS;
00111
00112
00113
00114
00115
00116
00117
00118 const double PI = 3.14159265358979323e0;
00119 const double PI_OVER_2 = (PI / 2.0e0);
00120 const double TWO_PI = (2.0e0 * PI);
00121 const double MAX_LAT = (61.5 * PI / 180.0);
00122 const double MIN_LAT = (49.5 * PI / 180.0);
00123 const double MAX_LON = (3.5 * PI / 180.0);
00124 const double MIN_LON = (-10.0 * PI / 180.0);
00125 const char* BNG500GRID = "STNOHJ";
00126 const char* BNG100GRID = "VWXYZQRSTULMNOPFGHJKABCDE";
00127
00128
00129 const double BNG_Origin_Lat = (49.0 * PI / 180.0);
00130 const double BNG_Origin_Long = (-2.0 * PI / 180.0);
00131 const double BNG_False_Northing = -100000.0;
00132 const double BNG_False_Easting = 400000.0;
00133 const double BNG_Scale_Factor = .9996012717;
00134
00135
00136 const double BNG_Max_Easting = 759961.0;
00137 const double BNG_Max_Northing = 1257875.0;
00138 const double BNG_Min_Easting = -133134.0;
00139 const double BNG_Min_Northing = -14829.0;
00140
00141 static const char* Airy = "AA";
00142
00143
00144
00145
00146
00147
00148
00149 void findIndex( char letter, const char* letterArray, long *index )
00150 {
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 long i = 0;
00162 long not_Found = 1;
00163 long length = strlen(letterArray);
00164
00165 while ((i < length) && (not_Found))
00166 {
00167 if (letter == letterArray[i])
00168 {
00169 *index = i;
00170 not_Found = 0;
00171 }
00172 i++;
00173 }
00174 if (not_Found)
00175 throw CoordinateConversionException( ErrorMessages::bngString );
00176 }
00177
00178
00179 long roundBNG( double value )
00180 {
00181
00182 double ivalue;
00183 long ival;
00184 double fraction = modf (value, &ivalue);
00185 ival = (long)(ivalue);
00186 if ((fraction > 0.5) || ((fraction == 0.5) && (ival%2 == 1)))
00187 ival++;
00188
00189 return (ival);
00190 }
00191
00192
00193 void makeBNGString( char ltrnum[4], long easting, long northing, char* BNGString, long precision )
00194
00195 {
00196
00197 double divisor;
00198 long east;
00199 long north;
00200 long i;
00201 long j;
00202 double unitInterval;
00203
00204 i = 0;
00205 for (j = 0; j < 3; j++)
00206 BNGString[i++] = ltrnum[j];
00207 divisor = pow (10.0, (5.0 - precision));
00208 unitInterval = pow (10.0, (double)precision);
00209
00210 east = roundBNG (easting/divisor);
00211 if (east == unitInterval)
00212 east -= 1;
00213 if ((precision == 0) && (east == 1))
00214 east = 0;
00215 i += sprintf (BNGString + i, "%*.*ld", precision, precision, east);
00216
00217 north = roundBNG (northing/divisor);
00218 if (north == unitInterval)
00219 north -= 1;
00220 if ((precision == 0) && (north == 1))
00221 north = 0;
00222 i += sprintf (BNGString + i, "%*.*ld", precision, precision, north);
00223 }
00224
00225
00226 bool checkOutOfArea( char BNG500, char BNG100 )
00227
00228 {
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239 bool outOfArea = false;
00240
00241 switch (BNG500)
00242 {
00243 case 'S':
00244 switch (BNG100)
00245 {
00246 case 'A':
00247 case 'F':
00248 case 'L':
00249 outOfArea = true;
00250 break;
00251 default:
00252 break;
00253 }
00254 break;
00255 case 'N':
00256 switch (BNG100)
00257 {
00258 case 'V':
00259 outOfArea = true;
00260 break;
00261 default:
00262 break;
00263 }
00264 break;
00265
00266 case 'H':
00267 if (BNG100 < 'L')
00268 outOfArea = true;
00269 break;
00270 case 'T':
00271 switch (BNG100)
00272 {
00273 case 'D':
00274 case 'E':
00275 case 'J':
00276 case 'K':
00277 case 'O':
00278 case 'P':
00279 case 'T':
00280 case 'U':
00281 case 'X':
00282 case 'Y':
00283 case 'Z':
00284 outOfArea = true;
00285 break;
00286 default:
00287 break;
00288 }
00289 break;
00290 case 'O':
00291 switch (BNG100)
00292 {
00293 case 'C':
00294 case 'D':
00295 case 'E':
00296 case 'J':
00297 case 'K':
00298 case 'O':
00299 case 'P':
00300 case 'T':
00301 case 'U':
00302 case 'Y':
00303 case 'Z':
00304 outOfArea = true;
00305 break;
00306 default:
00307 break;
00308 }
00309 break;
00310 case 'J':
00311 switch (BNG100)
00312 {
00313 case 'L':
00314 case 'M':
00315 case 'Q':
00316 case 'R':
00317 case 'V':
00318 case 'W':
00320 break;
00321 default:
00322 outOfArea = true;
00323 break;
00324 }
00325 break;
00326 default:
00327 outOfArea = true;
00328 break;
00329 }
00330
00331 return outOfArea;
00332 }
00333
00334
00335 void breakBNGString( char* BNGString, char letters[3], double* easting, double* northing, long* precision )
00336 {
00337
00338
00339 long i = 0;
00340 long j;
00341 long num_digits = 0;
00342 long num_letters;
00343 long length = strlen(BNGString);
00344
00345 while (BNGString[i] == ' ')
00346 i++;
00347 j = i;
00348 while (isalpha(BNGString[i]))
00349 i++;
00350 num_letters = i - j;
00351 if (num_letters == 2)
00352 {
00353
00354 letters[0] = (char)toupper(BNGString[j]);
00355 letters[1] = (char)toupper(BNGString[j+1]);
00356 letters[2] = 0;
00357 }
00358 else
00359 throw CoordinateConversionException( ErrorMessages::bngString );
00360
00361 checkOutOfArea(letters[0], letters[1]);
00362 while (BNGString[i] == ' ')
00363 i++;
00364 j = i;
00365 if (BNGString[length-1] == ' ')
00366 length --;
00367 while (i < length)
00368 {
00369 if (isdigit(BNGString[i]))
00370 i++;
00371 else
00372 throw CoordinateConversionException( ErrorMessages::bngString );
00373 }
00374
00375 num_digits = i - j;
00376 if ((num_digits <= 10) && (num_digits%2 == 0))
00377 {
00378 long n;
00379 char east_string[6];
00380 char north_string[6];
00381 long east;
00382 long north;
00383 double multiplier;
00384
00385
00386 n = num_digits / 2;
00387 *precision = n;
00388 if (n > 0)
00389 {
00390 strncpy (east_string, BNGString+j, n);
00391 east_string[n] = 0;
00392 sscanf (east_string, "%ld", &east);
00393 strncpy (north_string, BNGString+j+n, n);
00394 north_string[n] = 0;
00395 sscanf (north_string, "%ld", &north);
00396 multiplier = pow (10.0, 5.0 - n);
00397 *easting = east * multiplier;
00398 *northing = north * multiplier;
00399 }
00400 else
00401 {
00402 *easting = 0.0;
00403 *northing = 0.0;
00404 }
00405 }
00406 else
00407 throw CoordinateConversionException( ErrorMessages::bngString );
00408 }
00409
00410
00411
00412
00413
00414
00415
00416 BritishNationalGrid::BritishNationalGrid( char *ellipsoidCode ) :
00417 CoordinateSystem( 6377563.396, 1 / 299.324964600 ),
00418 transverseMercator( 0 ),
00419 BNG_Easting( 0.0 ),
00420 BNG_Northing( 0.0 )
00421 {
00422
00423
00424
00425
00426
00427
00428
00429
00430 strcpy( BNG_Letters, "SV" );
00431 strcpy( BNG_Ellipsoid_Code, "AA");
00432
00433 if ( strcmp( ellipsoidCode, Airy ) != 0 )
00434 {
00435 throw CoordinateConversionException( ErrorMessages::bngEllipsoid );
00436 }
00437
00438 strcpy( BNG_Ellipsoid_Code, ellipsoidCode );
00439
00440 transverseMercator = new TransverseMercator( semiMajorAxis, flattening, BNG_Origin_Long, BNG_Origin_Lat,
00441 BNG_False_Easting, BNG_False_Northing, BNG_Scale_Factor );
00442 }
00443
00444
00445 BritishNationalGrid::BritishNationalGrid( const BritishNationalGrid &bng )
00446 {
00447 transverseMercator = new TransverseMercator( *( bng.transverseMercator ) );
00448 semiMajorAxis = bng.semiMajorAxis;
00449 flattening = bng.flattening;
00450 BNG_Easting = bng.BNG_Easting;
00451 BNG_Northing = bng.BNG_Northing;
00452 }
00453
00454
00455 BritishNationalGrid::~BritishNationalGrid()
00456 {
00457 delete transverseMercator;
00458 transverseMercator = 0;
00459 }
00460
00461
00462 BritishNationalGrid& BritishNationalGrid::operator=( const BritishNationalGrid &bng )
00463 {
00464 if( this != &bng )
00465 {
00466 transverseMercator->operator=( *bng.transverseMercator );
00467 semiMajorAxis = bng.semiMajorAxis;
00468 flattening = bng.flattening;
00469 BNG_Easting = bng.BNG_Easting;
00470 BNG_Northing = bng.BNG_Northing;
00471 }
00472
00473 return *this;
00474 }
00475
00476
00477 EllipsoidParameters* BritishNationalGrid::getParameters() const
00478 {
00479
00480
00481
00482
00483
00484
00485
00486 return new EllipsoidParameters( semiMajorAxis, flattening, (char*)BNG_Ellipsoid_Code );
00487 }
00488
00489
00490 MSP::CCS::BNGCoordinates* BritishNationalGrid::convertFromGeodetic( MSP::CCS::GeodeticCoordinates* geodeticCoordinates, long precision )
00491 {
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505 double TMEasting, TMNorthing;
00506 char errorStatus[50] = "";
00507
00508 double longitude = geodeticCoordinates->longitude();
00509 double latitude = geodeticCoordinates->latitude();
00510
00511 if ((latitude < MIN_LAT) || (latitude > MAX_LAT))
00512 {
00513 strcat( errorStatus, MSP::CCS::ErrorMessages::latitude );
00514 }
00515 if ((longitude < MIN_LON) || (longitude > MAX_LON))
00516 {
00517 strcat( errorStatus, MSP::CCS::ErrorMessages::longitude );
00518 }
00519
00520 if( strlen( errorStatus ) > 0)
00521 throw CoordinateConversionException( errorStatus );
00522
00523 MapProjectionCoordinates* transverseMercatorCoordinates = transverseMercator->convertFromGeodetic( geodeticCoordinates );
00524
00525 TMEasting = transverseMercatorCoordinates->easting();
00526 TMNorthing = transverseMercatorCoordinates->northing();
00527
00528 if ((TMEasting < 0.0) && (TMEasting > -2.0))
00529 transverseMercatorCoordinates->setEasting( 0.0 );
00530 if ((TMNorthing < 0.0) && (TMNorthing > -2.0))
00531 transverseMercatorCoordinates->setNorthing( 0.0 );
00532
00533 TMEasting = transverseMercatorCoordinates->easting();
00534 TMNorthing = transverseMercatorCoordinates->northing();
00535
00536 if ((TMEasting < BNG_Min_Easting) || (TMEasting > BNG_Max_Easting))
00537 {
00538 delete transverseMercatorCoordinates;
00539 throw CoordinateConversionException( ErrorMessages::invalidArea );
00540 }
00541 if ((TMNorthing < BNG_Min_Northing) || (TMNorthing > BNG_Max_Northing))
00542 {
00543 delete transverseMercatorCoordinates;
00544 throw CoordinateConversionException( ErrorMessages::invalidArea );
00545 }
00546
00547 BNGCoordinates* bngCoordinates = convertFromTransverseMercator( transverseMercatorCoordinates, precision );
00548 delete transverseMercatorCoordinates;
00549
00550 return bngCoordinates;
00551 }
00552
00553
00554 MSP::CCS::GeodeticCoordinates* BritishNationalGrid::convertToGeodetic( MSP::CCS::BNGCoordinates* bngCoordinates )
00555 {
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568 double TMEasting, TMNorthing;
00569 long in_Precision;
00570
00571 char* BNGString = bngCoordinates->BNGString();
00572
00573 breakBNGString( BNGString, BNG_Letters, &BNG_Easting, &BNG_Northing, &in_Precision );
00574
00575 MapProjectionCoordinates* transverseMercatorCoordinates = convertToTransverseMercator( bngCoordinates );
00576 TMEasting = transverseMercatorCoordinates->easting();
00577 TMNorthing = transverseMercatorCoordinates->northing();
00578
00579 if ((TMEasting < BNG_Min_Easting) || (TMEasting > BNG_Max_Easting))
00580 {
00581 delete transverseMercatorCoordinates;
00582 throw CoordinateConversionException( ErrorMessages::invalidArea );
00583 }
00584 if ((TMNorthing < BNG_Min_Northing) || (TMNorthing > BNG_Max_Northing))
00585 {
00586 delete transverseMercatorCoordinates;
00587 throw CoordinateConversionException( ErrorMessages::invalidArea );
00588 }
00589
00590 GeodeticCoordinates* geodeticCoordinates = transverseMercator->convertToGeodetic( transverseMercatorCoordinates );
00591 double latitude = geodeticCoordinates->latitude();
00592 double longitude = geodeticCoordinates->longitude();
00593 delete transverseMercatorCoordinates;
00594
00595 if ((latitude < MIN_LAT) || (latitude > MAX_LAT))
00596 {
00597 delete geodeticCoordinates;
00598 throw CoordinateConversionException( ErrorMessages::invalidArea );
00599 }
00600 if ((longitude < MIN_LON) || (longitude > MAX_LON))
00601 {
00602 delete geodeticCoordinates;
00603 throw CoordinateConversionException( ErrorMessages::invalidArea );
00604 }
00605
00606 return geodeticCoordinates;
00607 }
00608
00609
00610 MSP::CCS::BNGCoordinates* BritishNationalGrid::convertFromTransverseMercator( MapProjectionCoordinates* mapProjectionCoordinates, long precision )
00611 {
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624 char letters[4];
00625 long x, y;
00626 long index;
00627 long temp_Easting, temp_Northing;
00628 char BNGString[21];
00629 char errorStatus[50] = "";
00630
00631 double easting = mapProjectionCoordinates->easting();
00632 double northing = mapProjectionCoordinates->northing();
00633
00634 if ((easting < BNG_Min_Easting) || (easting > BNG_Max_Easting))
00635 {
00636 strcat( errorStatus, ErrorMessages::easting );
00637 }
00638 if ((northing < BNG_Min_Northing) || (northing > BNG_Max_Northing))
00639 {
00640 strcat( errorStatus, ErrorMessages::northing );
00641 }
00642
00643 if( strlen( errorStatus ) > 0)
00644 throw CoordinateConversionException( errorStatus );
00645
00646 temp_Easting = roundBNG(easting);
00647 temp_Northing = roundBNG(northing);
00648
00649 temp_Easting += 1000000;
00650 temp_Northing += 500000;
00651
00652 x = temp_Easting / 500000;
00653 y = temp_Northing / 500000;
00654
00655 index = y * 5 + x;
00656 if ((index >= 0) && (index < 25))
00657 {
00658 letters[0] = BNG100GRID[index];
00659 temp_Easting %= 500000;
00660 temp_Northing %= 500000;
00661 x = temp_Easting / 100000;
00662 y = temp_Northing / 100000;
00663 index = y * 5 + x;
00664 if ((index >= 0) && (index < 25))
00665 {
00666 letters[1] = BNG100GRID[index];
00667
00668 if( checkOutOfArea(letters[0], letters[1]) )
00669 throw CoordinateConversionException( ErrorMessages::invalidArea );
00670
00671 letters[2] = ' ';
00672 letters[3] = 0;
00673 temp_Easting %= 100000;
00674 temp_Northing %= 100000;
00675 makeBNGString(letters, temp_Easting, temp_Northing, BNGString, precision);
00676 }
00677 else
00678 {
00679 long five_y = 5 * y;
00680 if ((x >= (25 - five_y)) || (x < -five_y))
00681 throw CoordinateConversionException( ErrorMessages::easting );
00682 if ((five_y >= (25 - x)) || (five_y < -x))
00683 throw CoordinateConversionException( ErrorMessages::northing );
00684 }
00685 }
00686 else
00687 {
00688 long five_y = 5 * y;
00689 if ((x >= (25 - five_y)) || (x < -five_y))
00690 throw CoordinateConversionException( ErrorMessages::easting );
00691 if ((five_y >= (25 - x)) || (five_y < -x))
00692 throw CoordinateConversionException( ErrorMessages::northing );
00693 }
00694
00695 return new BNGCoordinates( CoordinateType::britishNationalGrid, BNGString );
00696 }
00697
00698
00699 MSP::CCS::MapProjectionCoordinates* BritishNationalGrid::convertToTransverseMercator( MSP::CCS::BNGCoordinates* bngCoordinates )
00700 {
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 long northing_Offset, easting_Offset;
00714 long i = 0;
00715 long j = 0;
00716 long in_Precision;
00717
00718 char* BNGString = bngCoordinates->BNGString();
00719
00720 breakBNGString( BNGString, BNG_Letters, &BNG_Easting, &BNG_Northing, &in_Precision );
00721
00722 findIndex(BNG_Letters[0], BNG500GRID, &i);
00723
00724 northing_Offset = 500000 * (i / 2);
00725 easting_Offset = 500000 * (i % 2);
00726
00727 findIndex(BNG_Letters[1], BNG100GRID, &j);
00728
00729 northing_Offset += 100000 * (j / 5);
00730 easting_Offset += 100000 * (j % 5);
00731
00732 double easting = BNG_Easting + easting_Offset;
00733 double northing = BNG_Northing + northing_Offset;
00734
00735 return new MapProjectionCoordinates( CoordinateType::transverseMercator, easting, northing );
00736 }
00737
00738
00739
00740
00741