national_grid 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a63473d5509a197a4561690220a04bd5fcd01ed3
4
- data.tar.gz: fdf94e39b6f7a4b96def05be79e8010a0135ed6a
3
+ metadata.gz: 8f41ecc57f8f9a7908bd0a4c0850bea186060eb1
4
+ data.tar.gz: 3db89a1bb90c25dd163d8cb3aaca85e5173c6ad9
5
5
  SHA512:
6
- metadata.gz: 7036cb2156ca35e3b81d48a14968160f936a8481d16f455d3717b51e0c713f4b20ad2280007f6ca716502bb180a0f34a09c0e827c00e565be1b952a2b62c1b60
7
- data.tar.gz: 17bdd7ef6e1dbfb352404958c1836b9298c3b36af18165892d08a6fe07e4f26ba079ba1e164c820eab657994c25866179a1bba2d68e6132280248a97e3641527
6
+ metadata.gz: b4f670c8624ce08e88170b9034625b2b6825a9084670d4743a5be1cfbd03acae012a7d9a9b50ce6730cfec87eca49f1323c1d9e20b2fc028c5509e08b23910d3
7
+ data.tar.gz: b6ad55bbbf7e5cf61a51af91d3b86ee93ebb6f95d30583b3a35811bc1a43fc2ede6b167bf929575ca16add6de57a0d1ae032567a97f9150a8fbd09911f846396
data/Rakefile CHANGED
@@ -5,16 +5,10 @@ require_relative "ext/ostn02c"
5
5
 
6
6
  CLEAN.include OSTN02C.object_path
7
7
 
8
- file OSTN02C.library => OSTN02C.sources do
9
- OSTN02C.make or fail "Building libostn02 failed"
10
- end
11
-
12
8
  Rake::ExtensionTask.new "national_grid" do |ext|
13
9
  ext.lib_dir = "lib/national_grid"
14
10
  end
15
11
 
16
- Rake::Task["compile"].prerequisites.unshift FileList[OSTN02C.library]
17
-
18
12
  Rake::TestTask.new "test" => "compile" do |test|
19
13
  test.pattern = "test/**/*_test.rb"
20
14
  end
@@ -3,6 +3,8 @@ require_relative "../ostn02c"
3
3
 
4
4
  dir_config "ostn02c", [OSTN02C.include_path], [OSTN02C.lib_path]
5
5
 
6
+ OSTN02C.make or abort "building libostn02 failed"
7
+
6
8
  abort "libostn02 not found" unless have_library "ostn02", "test(false)", "OSTN02.h"
7
9
 
8
10
  create_makefile "national_grid/national_grid"
@@ -12,14 +12,14 @@ module OSTN02C
12
12
  puts "Compiling libostn02"
13
13
  FileUtils.mkdir_p object_path
14
14
  Dir.chdir object_path do
15
- system RbConfig.expand("$(CC) -I#{include_path} $(CPPFLAGS) $(CFLAGS) -c #{sources.join(" ")}")
15
+ run RbConfig.expand("$(CC) -I#{include_path} $(CPPFLAGS) $(CFLAGS) -c #{sources.join(" ")}")
16
16
  end
17
17
  end
18
18
 
19
19
  def package
20
20
  puts "Packaging libostn02"
21
21
  FileUtils.mkdir_p lib_path
22
- system RbConfig.expand("$(AR) -crsv #{library} #{objects}")
22
+ run RbConfig.expand("$(AR) -crsv #{library} #{objects}")
23
23
  end
24
24
 
25
25
  def sources
@@ -49,4 +49,9 @@ module OSTN02C
49
49
  def output_path
50
50
  File.expand_path("../tmp/ostn02c", __dir__)
51
51
  end
52
+
53
+ def run(command)
54
+ puts command
55
+ system command
56
+ end
52
57
  end
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ xcuserdata
@@ -0,0 +1,280 @@
1
+ // !$*UTF8*$!
2
+ {
3
+ archiveVersion = 1;
4
+ classes = {
5
+ };
6
+ objectVersion = 46;
7
+ objects = {
8
+
9
+ /* Begin PBXBuildFile section */
10
+ 5E2FEDCD14B35558007849B4 /* crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = 5E2FEDCC14B35558007849B4 /* crc32.c */; };
11
+ 5EBE804514A5FE4800CA9D70 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE804414A5FE4800CA9D70 /* main.c */; };
12
+ 5EBE804F14A5FE9000CA9D70 /* OSTN02.c in Sources */ = {isa = PBXBuildFile; fileRef = 5EBE804E14A5FE9000CA9D70 /* OSTN02.c */; };
13
+ /* End PBXBuildFile section */
14
+
15
+ /* Begin PBXCopyFilesBuildPhase section */
16
+ 5EBE803E14A5FE4800CA9D70 /* CopyFiles */ = {
17
+ isa = PBXCopyFilesBuildPhase;
18
+ buildActionMask = 2147483647;
19
+ dstPath = /usr/share/man/man1/;
20
+ dstSubfolderSpec = 0;
21
+ files = (
22
+ );
23
+ runOnlyForDeploymentPostprocessing = 1;
24
+ };
25
+ /* End PBXCopyFilesBuildPhase section */
26
+
27
+ /* Begin PBXFileReference section */
28
+ 5E2FEDCC14B35558007849B4 /* crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = crc32.c; sourceTree = "<group>"; };
29
+ 5E2FEDCE14B35570007849B4 /* crc32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crc32.h; sourceTree = "<group>"; };
30
+ 5E2FEDCF14B35DD4007849B4 /* testCoords.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = testCoords.data; sourceTree = "<group>"; };
31
+ 5E2FEDDA14B3641B007849B4 /* geoids.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = geoids.data; sourceTree = "<group>"; };
32
+ 5E2FEDDD14B374AF007849B4 /* README.textile */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.textile; sourceTree = "<group>"; };
33
+ 5E67D47B14B212FE00F5D4BC /* shifts.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = shifts.data; sourceTree = "<group>"; };
34
+ 5E67D47C14B212FE00F5D4BC /* shifts.index.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = shifts.index.data; sourceTree = "<group>"; };
35
+ 5E7AD48014B0D26E00AA6C99 /* csv-to-struct-array.rb */ = {isa = PBXFileReference; lastKnownFileType = text.script.ruby; path = "csv-to-struct-array.rb"; sourceTree = "<group>"; };
36
+ 5E7BD21415B99A5F0003C1E5 /* explorerMaps.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = explorerMaps.data; sourceTree = "<group>"; };
37
+ 5E9AFE1214F2525900F82BD7 /* fancyOut.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fancyOut.h; sourceTree = "<group>"; };
38
+ 5E9AFE1414F2560D00F82BD7 /* dblRelated.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dblRelated.h; sourceTree = "<group>"; };
39
+ 5EB2BE9D14B7593100EE7A5D /* gridRef.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = gridRef.data; sourceTree = "<group>"; };
40
+ 5EBE804014A5FE4800CA9D70 /* ostn02c */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ostn02c; sourceTree = BUILT_PRODUCTS_DIR; };
41
+ 5EBE804414A5FE4800CA9D70 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
42
+ 5EBE804D14A5FE7300CA9D70 /* OSTN02.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSTN02.h; sourceTree = "<group>"; };
43
+ 5EBE804E14A5FE9000CA9D70 /* OSTN02.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = OSTN02.c; sourceTree = "<group>"; };
44
+ 5ECB2EE414B863F00015B28A /* constants.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = constants.data; sourceTree = "<group>"; };
45
+ /* End PBXFileReference section */
46
+
47
+ /* Begin PBXFrameworksBuildPhase section */
48
+ 5EBE803D14A5FE4800CA9D70 /* Frameworks */ = {
49
+ isa = PBXFrameworksBuildPhase;
50
+ buildActionMask = 2147483647;
51
+ files = (
52
+ );
53
+ runOnlyForDeploymentPostprocessing = 0;
54
+ };
55
+ /* End PBXFrameworksBuildPhase section */
56
+
57
+ /* Begin PBXGroup section */
58
+ 5E2FEDDC14B364D6007849B4 /* Data */ = {
59
+ isa = PBXGroup;
60
+ children = (
61
+ 5E67D47B14B212FE00F5D4BC /* shifts.data */,
62
+ 5E67D47C14B212FE00F5D4BC /* shifts.index.data */,
63
+ 5E2FEDCF14B35DD4007849B4 /* testCoords.data */,
64
+ 5E2FEDDA14B3641B007849B4 /* geoids.data */,
65
+ 5EB2BE9D14B7593100EE7A5D /* gridRef.data */,
66
+ 5ECB2EE414B863F00015B28A /* constants.data */,
67
+ 5E7BD21415B99A5F0003C1E5 /* explorerMaps.data */,
68
+ );
69
+ name = Data;
70
+ path = OSTN02;
71
+ sourceTree = "<group>";
72
+ };
73
+ 5EBE803514A5FE4800CA9D70 = {
74
+ isa = PBXGroup;
75
+ children = (
76
+ 5E2FEDDD14B374AF007849B4 /* README.textile */,
77
+ 5E7AD48014B0D26E00AA6C99 /* csv-to-struct-array.rb */,
78
+ 5EBE804314A5FE4800CA9D70 /* Source code */,
79
+ 5E2FEDDC14B364D6007849B4 /* Data */,
80
+ 5EBE804114A5FE4800CA9D70 /* Products */,
81
+ );
82
+ sourceTree = "<group>";
83
+ };
84
+ 5EBE804114A5FE4800CA9D70 /* Products */ = {
85
+ isa = PBXGroup;
86
+ children = (
87
+ 5EBE804014A5FE4800CA9D70 /* ostn02c */,
88
+ );
89
+ name = Products;
90
+ sourceTree = "<group>";
91
+ };
92
+ 5EBE804314A5FE4800CA9D70 /* Source code */ = {
93
+ isa = PBXGroup;
94
+ children = (
95
+ 5EBE804D14A5FE7300CA9D70 /* OSTN02.h */,
96
+ 5EBE804E14A5FE9000CA9D70 /* OSTN02.c */,
97
+ 5E2FEDCC14B35558007849B4 /* crc32.c */,
98
+ 5E2FEDCE14B35570007849B4 /* crc32.h */,
99
+ 5EBE804414A5FE4800CA9D70 /* main.c */,
100
+ 5E9AFE1214F2525900F82BD7 /* fancyOut.h */,
101
+ 5E9AFE1414F2560D00F82BD7 /* dblRelated.h */,
102
+ );
103
+ name = "Source code";
104
+ path = OSTN02;
105
+ sourceTree = "<group>";
106
+ };
107
+ /* End PBXGroup section */
108
+
109
+ /* Begin PBXNativeTarget section */
110
+ 5EBE803F14A5FE4800CA9D70 /* OSTN02 */ = {
111
+ isa = PBXNativeTarget;
112
+ buildConfigurationList = 5EBE804A14A5FE4900CA9D70 /* Build configuration list for PBXNativeTarget "OSTN02" */;
113
+ buildPhases = (
114
+ 5EBE803C14A5FE4800CA9D70 /* Sources */,
115
+ 5EBE803D14A5FE4800CA9D70 /* Frameworks */,
116
+ 5EBE803E14A5FE4800CA9D70 /* CopyFiles */,
117
+ );
118
+ buildRules = (
119
+ );
120
+ dependencies = (
121
+ );
122
+ name = OSTN02;
123
+ productName = OSTN02;
124
+ productReference = 5EBE804014A5FE4800CA9D70 /* ostn02c */;
125
+ productType = "com.apple.product-type.tool";
126
+ };
127
+ /* End PBXNativeTarget section */
128
+
129
+ /* Begin PBXProject section */
130
+ 5EBE803714A5FE4800CA9D70 /* Project object */ = {
131
+ isa = PBXProject;
132
+ attributes = {
133
+ LastUpgradeCheck = 0430;
134
+ ORGANIZATIONNAME = "George MacKerron";
135
+ };
136
+ buildConfigurationList = 5EBE803A14A5FE4800CA9D70 /* Build configuration list for PBXProject "OSTN02" */;
137
+ compatibilityVersion = "Xcode 3.2";
138
+ developmentRegion = English;
139
+ hasScannedForEncodings = 0;
140
+ knownRegions = (
141
+ en,
142
+ );
143
+ mainGroup = 5EBE803514A5FE4800CA9D70;
144
+ productRefGroup = 5EBE804114A5FE4800CA9D70 /* Products */;
145
+ projectDirPath = "";
146
+ projectRoot = "";
147
+ targets = (
148
+ 5EBE803F14A5FE4800CA9D70 /* OSTN02 */,
149
+ );
150
+ };
151
+ /* End PBXProject section */
152
+
153
+ /* Begin PBXSourcesBuildPhase section */
154
+ 5EBE803C14A5FE4800CA9D70 /* Sources */ = {
155
+ isa = PBXSourcesBuildPhase;
156
+ buildActionMask = 2147483647;
157
+ files = (
158
+ 5EBE804514A5FE4800CA9D70 /* main.c in Sources */,
159
+ 5EBE804F14A5FE9000CA9D70 /* OSTN02.c in Sources */,
160
+ 5E2FEDCD14B35558007849B4 /* crc32.c in Sources */,
161
+ );
162
+ runOnlyForDeploymentPostprocessing = 0;
163
+ };
164
+ /* End PBXSourcesBuildPhase section */
165
+
166
+ /* Begin XCBuildConfiguration section */
167
+ 5EBE804814A5FE4800CA9D70 /* Debug */ = {
168
+ isa = XCBuildConfiguration;
169
+ buildSettings = {
170
+ ALWAYS_SEARCH_USER_PATHS = NO;
171
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
172
+ CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
173
+ COPY_PHASE_STRIP = NO;
174
+ GCC_C_LANGUAGE_STANDARD = gnu99;
175
+ GCC_DYNAMIC_NO_PIC = NO;
176
+ GCC_ENABLE_OBJC_EXCEPTIONS = NO;
177
+ GCC_OPTIMIZATION_LEVEL = 0;
178
+ GCC_PREPROCESSOR_DEFINITIONS = (
179
+ "DEBUG=1",
180
+ "$(inherited)",
181
+ );
182
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
183
+ GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
184
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
185
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
186
+ GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
187
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
188
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
189
+ GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
190
+ GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
191
+ GCC_WARN_SHADOW = YES;
192
+ GCC_WARN_SIGN_COMPARE = YES;
193
+ GCC_WARN_UNKNOWN_PRAGMAS = YES;
194
+ GCC_WARN_UNUSED_FUNCTION = YES;
195
+ GCC_WARN_UNUSED_PARAMETER = YES;
196
+ GCC_WARN_UNUSED_VARIABLE = YES;
197
+ MACOSX_DEPLOYMENT_TARGET = 10.4;
198
+ ONLY_ACTIVE_ARCH = YES;
199
+ SDKROOT = macosx;
200
+ };
201
+ name = Debug;
202
+ };
203
+ 5EBE804914A5FE4800CA9D70 /* Release */ = {
204
+ isa = XCBuildConfiguration;
205
+ buildSettings = {
206
+ ALWAYS_SEARCH_USER_PATHS = NO;
207
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
208
+ CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
209
+ COPY_PHASE_STRIP = YES;
210
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
211
+ GCC_C_LANGUAGE_STANDARD = gnu99;
212
+ GCC_ENABLE_OBJC_EXCEPTIONS = NO;
213
+ GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
214
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
215
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
216
+ GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
217
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
218
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
219
+ GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
220
+ GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
221
+ GCC_WARN_SHADOW = YES;
222
+ GCC_WARN_SIGN_COMPARE = YES;
223
+ GCC_WARN_UNKNOWN_PRAGMAS = YES;
224
+ GCC_WARN_UNUSED_FUNCTION = YES;
225
+ GCC_WARN_UNUSED_PARAMETER = YES;
226
+ GCC_WARN_UNUSED_VARIABLE = YES;
227
+ MACOSX_DEPLOYMENT_TARGET = 10.4;
228
+ RUN_CLANG_STATIC_ANALYZER = YES;
229
+ SDKROOT = macosx;
230
+ VALIDATE_PRODUCT = YES;
231
+ };
232
+ name = Release;
233
+ };
234
+ 5EBE804B14A5FE4900CA9D70 /* Debug */ = {
235
+ isa = XCBuildConfiguration;
236
+ buildSettings = {
237
+ LIBRARY_SEARCH_PATHS = (
238
+ "$(inherited)",
239
+ "\"$(SDKROOT)/usr/lib/system\"",
240
+ );
241
+ PRODUCT_NAME = ostn02c;
242
+ };
243
+ name = Debug;
244
+ };
245
+ 5EBE804C14A5FE4900CA9D70 /* Release */ = {
246
+ isa = XCBuildConfiguration;
247
+ buildSettings = {
248
+ LIBRARY_SEARCH_PATHS = (
249
+ "$(inherited)",
250
+ "\"$(SDKROOT)/usr/lib/system\"",
251
+ );
252
+ PRODUCT_NAME = ostn02c;
253
+ };
254
+ name = Release;
255
+ };
256
+ /* End XCBuildConfiguration section */
257
+
258
+ /* Begin XCConfigurationList section */
259
+ 5EBE803A14A5FE4800CA9D70 /* Build configuration list for PBXProject "OSTN02" */ = {
260
+ isa = XCConfigurationList;
261
+ buildConfigurations = (
262
+ 5EBE804814A5FE4800CA9D70 /* Debug */,
263
+ 5EBE804914A5FE4800CA9D70 /* Release */,
264
+ );
265
+ defaultConfigurationIsVisible = 0;
266
+ defaultConfigurationName = Release;
267
+ };
268
+ 5EBE804A14A5FE4900CA9D70 /* Build configuration list for PBXNativeTarget "OSTN02" */ = {
269
+ isa = XCConfigurationList;
270
+ buildConfigurations = (
271
+ 5EBE804B14A5FE4900CA9D70 /* Debug */,
272
+ 5EBE804C14A5FE4900CA9D70 /* Release */,
273
+ );
274
+ defaultConfigurationIsVisible = 0;
275
+ defaultConfigurationName = Release;
276
+ };
277
+ /* End XCConfigurationList section */
278
+ };
279
+ rootObject = 5EBE803714A5FE4800CA9D70 /* Project object */;
280
+ }
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ <FileRef
5
+ location = "self:OSTN02.xcodeproj">
6
+ </FileRef>
7
+ </Workspace>
@@ -0,0 +1,510 @@
1
+ //
2
+ // OSTN02.c
3
+ // OSTN02
4
+ //
5
+ // Created by George MacKerron on 24/12/2011.
6
+ // Copyright (c) 2011 George MacKerron. All rights reserved.
7
+ //
8
+
9
+ #include "OSTN02.h"
10
+ #include "dblRelated.h"
11
+ #include "crc32.h"
12
+ #include "fancyOut.h"
13
+ #include "constants.data"
14
+ #include "shifts.index.data"
15
+ #include "shifts.data"
16
+ #include "geoids.data"
17
+ #include "gridRef.data"
18
+ #include "testCoords.data"
19
+ #include "explorerMaps.data"
20
+
21
+ #define LENGTH_OF(x) (sizeof (x) / sizeof *(x))
22
+ #define ASPRINTF_OR_DIE(...) if (asprintf(__VA_ARGS__) < 0) exit(EXIT_FAILURE)
23
+
24
+ #define originalIndicesCRC 244629328L // these won't be robust against differing endianness or compiler packing of bit-structs
25
+ #define originalDataCRC 790474494L
26
+
27
+ static CDBL piOver180 = L(0.017453292519943295769236907684886127134428718885417254560971914401710091146034494436822415696345094822);
28
+ static CDBL oneEightyOverPi = L(57.29577951308232087679815481410517033240547246656432154916024386120284714832155263244096899585111094418);
29
+
30
+
31
+ DBL gridConvergenceDegreesFromLatLon(const LatLonDecimal latLon, const Ellipsoid ellipsoid, const MapProjection projection) {
32
+
33
+ CDBL phi = piOver180 * latLon.lat;
34
+ CDBL lambda = piOver180 * latLon.lon;
35
+ CDBL lambda0 = piOver180 * projection.trueOriginLatLon.lon;
36
+
37
+ CDBL a = ellipsoid.semiMajorAxis;
38
+ CDBL b = ellipsoid.semiMinorAxis;
39
+ CDBL f0 = projection.centralMeridianScale;
40
+
41
+ CDBL deltaLambda = lambda - lambda0;
42
+ CDBL deltaLambda2 = deltaLambda * deltaLambda;
43
+ CDBL deltaLambda3 = deltaLambda2 * deltaLambda;
44
+ CDBL deltaLambda5 = deltaLambda3 * deltaLambda2;
45
+
46
+ CDBL sinPhi = SIN(phi);
47
+ CDBL sinPhi2 = sinPhi * sinPhi;
48
+ CDBL cosPhi = COS(phi);
49
+ CDBL cosPhi2 = cosPhi * cosPhi;
50
+ CDBL cosPhi4 = cosPhi2 * cosPhi2;
51
+ CDBL tanPhi = TAN(phi);
52
+ CDBL tanPhi2 = tanPhi * tanPhi;
53
+
54
+ CDBL af0 = a * f0;
55
+ CDBL af02 = af0 * af0;
56
+ CDBL bf0 = b * f0;
57
+ CDBL bf02 = bf0 * bf0;
58
+
59
+ CDBL e2 = (af02 - bf02) / af02;
60
+ CDBL nu = af0 / SQRT(L(1.0) - (e2 * sinPhi2));
61
+ CDBL rho = (nu * (L(1.0) - e2)) / (L(1.0) - (e2 * sinPhi2));
62
+ CDBL eta2 = (nu / rho) - L(1.0);
63
+ CDBL xiv = ((sinPhi * cosPhi2) / L(3.0)) * (L(1.0) + L(3.0) * eta2 + L(2.0) * eta2 * eta2);
64
+ CDBL xv = ((sinPhi * cosPhi4) / L(15.0)) * (L(2.0) - tanPhi2);
65
+
66
+ CDBL cRads = (deltaLambda * sinPhi) + (deltaLambda3 * xiv) + (deltaLambda5 * xv);
67
+ return oneEightyOverPi * cRads;
68
+ }
69
+
70
+
71
+ DBL gridConvergenceDegreesFromEastingNorthing(const EastingNorthing en, const Ellipsoid ellipsoid, const MapProjection projection) {
72
+
73
+ LatLonDecimal latLon = latLonFromEastingNorthing(en, ellipsoid, projection);
74
+ CDBL phi = piOver180 * latLon.lat;
75
+ CDBL lambda = piOver180 * latLon.lon;
76
+ CDBL lambda0 = piOver180 * projection.trueOriginLatLon.lon;
77
+
78
+ CDBL a = ellipsoid.semiMajorAxis;
79
+ CDBL b = ellipsoid.semiMinorAxis;
80
+ CDBL f0 = projection.centralMeridianScale;
81
+
82
+ CDBL deltaLambda = lambda - lambda0;
83
+ CDBL deltaLambda2 = deltaLambda * deltaLambda;
84
+ CDBL deltaLambda3 = deltaLambda2 * deltaLambda;
85
+ CDBL deltaLambda5 = deltaLambda3 * deltaLambda2;
86
+
87
+ CDBL sinPhi = SIN(phi);
88
+ CDBL sinPhi2 = sinPhi * sinPhi;
89
+ CDBL cosPhi = COS(phi);
90
+ CDBL cosPhi2 = cosPhi * cosPhi;
91
+ CDBL cosPhi4 = cosPhi2 * cosPhi2;
92
+ CDBL tanPhi = TAN(phi);
93
+ CDBL tanPhi2 = tanPhi * tanPhi;
94
+
95
+ CDBL af0 = a * f0;
96
+ CDBL af02 = af0 * af0;
97
+ CDBL bf0 = b * f0;
98
+ CDBL bf02 = bf0 * bf0;
99
+
100
+ CDBL e2 = (af02 - bf02) / af02;
101
+ CDBL nu = af0 / SQRT(L(1.0) - (e2 * sinPhi2));
102
+ CDBL rho = (nu * (L(1.0) - e2)) / (L(1.0) - (e2 * sinPhi2));
103
+ CDBL eta2 = (nu / rho) - L(1.0);
104
+ CDBL xiv = ((sinPhi * cosPhi2) / L(3.0)) * (L(1.0) + L(3.0) * eta2 + L(2.0) * eta2 * eta2);
105
+ CDBL xv = ((sinPhi * cosPhi4) / L(15.0)) * (L(2.0) - tanPhi2);
106
+
107
+ CDBL cRads = (deltaLambda * sinPhi) + (deltaLambda3 * xiv) + (deltaLambda5 * xv);
108
+ return oneEightyOverPi * cRads;
109
+ }
110
+
111
+ int nextOSExplorerMap(EastingNorthing en, int prevMap) {
112
+ for (int i = prevMap + 1, len = LENGTH_OF(OSExplorerMaps); i < len; i ++) {
113
+ OSMap map = OSExplorerMaps[i];
114
+ if ( en.e >= (DBL) map.emin
115
+ && en.e <= (DBL) map.emax
116
+ && en.n >= (DBL) map.nmin
117
+ && en.n <= (DBL) map.nmax) return i;
118
+ }
119
+ return -1;
120
+ }
121
+
122
+ EastingNorthing eastingNorthingFromLatLon(const LatLonDecimal latLon, const Ellipsoid ellipsoid, const MapProjection projection) {
123
+ CDBL a = ellipsoid.semiMajorAxis;
124
+ CDBL b = ellipsoid.semiMinorAxis;
125
+ CDBL f0 = projection.centralMeridianScale;
126
+ CDBL e0 = projection.trueOriginEastingNorthing.e;
127
+ CDBL n0 = projection.trueOriginEastingNorthing.n;
128
+
129
+ CDBL phi = piOver180 * latLon.lat;
130
+ CDBL lambda = piOver180 * latLon.lon;
131
+ CDBL phi0 = piOver180 * projection.trueOriginLatLon.lat;
132
+ CDBL lambda0 = piOver180 * projection.trueOriginLatLon.lon;
133
+
134
+ CDBL deltaPhi = phi - phi0;
135
+ CDBL sumPhi = phi + phi0;
136
+ CDBL sinPhi = SIN(phi);
137
+ CDBL sinPhi2 = sinPhi * sinPhi;
138
+ CDBL cosPhi = COS(phi);
139
+ CDBL cosPhi2 = cosPhi * cosPhi;
140
+ CDBL cosPhi3 = cosPhi2 * cosPhi;
141
+ CDBL cosPhi5 = cosPhi3 * cosPhi2;
142
+ CDBL tanPhi = TAN(phi);
143
+ CDBL tanPhi2 = tanPhi * tanPhi;
144
+ CDBL tanPhi4 = tanPhi2 * tanPhi2;
145
+
146
+ CDBL a2 = a * a;
147
+ CDBL e2 = (a2 - b * b) / a2;
148
+ CDBL n = (a - b) / (a + b);
149
+ CDBL n2 = n * n;
150
+ CDBL n3 = n2 * n;
151
+ CDBL oneMinusE2SinPhi2 = L(1.0) - e2 * sinPhi2;
152
+ CDBL sqrtOneMinusE2SinPhi2 = SQRT(oneMinusE2SinPhi2);
153
+ CDBL v = a * f0 / sqrtOneMinusE2SinPhi2;
154
+ CDBL rho = a * f0 * (L(1.0) - e2) / (oneMinusE2SinPhi2 * sqrtOneMinusE2SinPhi2);
155
+ CDBL eta2 = v / rho - L(1.0);
156
+ CDBL m = b * f0 * ( (L(1.0) + n + (L(5.0) / L(4.0)) * n2 + (L(5.0) / L(4.0)) * n3) * deltaPhi
157
+ - (L(3.0) * n + L(3.0) * n2 + (L(21.0) / L(8.0)) * n3) * SIN(deltaPhi) * COS(sumPhi)
158
+ + ((L(15.0) / L(8.0)) * n2 + (L(15.0) / L(8.0)) * n3) * SIN(L(2.0) * deltaPhi) * COS(L(2.0) * sumPhi)
159
+ - (L(35.0) / L(24.0)) * n3 * SIN(L(3.0) * deltaPhi) * COS(L(3.0) * sumPhi)
160
+ );
161
+
162
+ CDBL one = m + n0;
163
+ CDBL two = (v / L(2.0)) * sinPhi * cosPhi;
164
+ CDBL three = (v / L(24.0)) * sinPhi * cosPhi3 * (L(5.0) - tanPhi2 + L(9.0) * eta2);
165
+ CDBL threeA = (v / L(720.0)) * sinPhi * cosPhi5 * (L(61.0) - L(58.0) * tanPhi2 + tanPhi4);
166
+ CDBL four = v * cosPhi;
167
+ CDBL five = (v / L(6.0)) * cosPhi3 * (v / rho - tanPhi2);
168
+ CDBL six = (v / L(120.0)) * cosPhi5 * (L(5.0) - L(18.0) * tanPhi2 + tanPhi4 + L(14.0) * eta2 - L(58.0) * tanPhi2 * eta2);
169
+
170
+ CDBL deltaLambda = lambda - lambda0;
171
+ CDBL deltaLambda2 = deltaLambda * deltaLambda;
172
+ CDBL deltaLambda3 = deltaLambda2 * deltaLambda;
173
+ CDBL deltaLambda4 = deltaLambda3 * deltaLambda;
174
+ CDBL deltaLambda5 = deltaLambda4 * deltaLambda;
175
+ CDBL deltaLambda6 = deltaLambda5 * deltaLambda;
176
+
177
+ EastingNorthing en;
178
+ en.n = one + two * deltaLambda2 + three * deltaLambda4 + threeA * deltaLambda6;
179
+ en.e = e0 + four * deltaLambda + five * deltaLambda3 + six * deltaLambda5;
180
+ en.elevation = latLon.elevation;
181
+ en.geoid = latLon.geoid;
182
+
183
+ return en;
184
+ }
185
+
186
+ EastingNorthing ETRS89EastingNorthingFromETRS89LatLon(const LatLonDecimal latLon) {
187
+ return eastingNorthingFromLatLon(latLon, GRS80Ellipsoid, NationalGridProj);
188
+ }
189
+
190
+ LatLonDecimal latLonFromEastingNorthing(const EastingNorthing en, const Ellipsoid ellipsoid, const MapProjection projection) {
191
+ CDBL a = ellipsoid.semiMajorAxis;
192
+ CDBL b = ellipsoid.semiMinorAxis;
193
+ CDBL f0 = projection.centralMeridianScale;
194
+ CDBL e0 = projection.trueOriginEastingNorthing.e;
195
+ CDBL n0 = projection.trueOriginEastingNorthing.n;
196
+ CDBL n = (a - b) / (a + b);
197
+ CDBL n2 = n * n;
198
+ CDBL n3 = n2 * n;
199
+ CDBL bf0 = b * f0;
200
+
201
+ CDBL phi0 = piOver180 * projection.trueOriginLatLon.lat;
202
+ CDBL lambda0 = piOver180 * projection.trueOriginLatLon.lon;
203
+
204
+ DBL phi = phi0; // this is phi' in the OS docs
205
+ DBL m = L(0.0);
206
+ DBL deltaPhi, sumPhi;
207
+ do {
208
+ phi = (en.n - n0 - m) / (a * f0) + phi;
209
+ deltaPhi = phi - phi0;
210
+ sumPhi = phi + phi0;
211
+ m = bf0 * ( (L(1.0) + n + (L(5.0) / L(4.0)) * n2 + (L(5.0) / L(4.0)) * n3) * deltaPhi
212
+ - (L(3.0) * n + L(3.0) * n2 + (L(21.0) / L(8.0)) * n3) * SIN(deltaPhi) * COS(sumPhi)
213
+ + ((L(15.0) / L(8.0)) * n2 + (L(15.0) / L(8.0)) * n3) * SIN(L(2.0) * deltaPhi) * COS(L(2.0) * sumPhi)
214
+ - (L(35.0) / L(24.0)) * n3 * SIN(L(3.0) * deltaPhi) * COS(L(3.0) * sumPhi)
215
+ );
216
+ } while (ABS(en.n - n0 - m) >= L(0.00001));
217
+
218
+ CDBL sinPhi = SIN(phi);
219
+ CDBL sinPhi2 = sinPhi * sinPhi;
220
+ CDBL cosPhi = COS(phi);
221
+ CDBL secPhi = L(1.0) / cosPhi;
222
+ CDBL tanPhi = TAN(phi);
223
+ CDBL tanPhi2 = tanPhi * tanPhi;
224
+ CDBL tanPhi4 = tanPhi2 * tanPhi2;
225
+ CDBL tanPhi6 = tanPhi4 * tanPhi2;
226
+
227
+ CDBL a2 = a * a;
228
+ CDBL e2 = (a2 - b * b) / a2;
229
+ CDBL oneMinusE2SinPhi2 = L(1.0) - e2 * sinPhi2;
230
+ CDBL sqrtOneMinusE2SinPhi2 = SQRT(oneMinusE2SinPhi2);
231
+ CDBL v = a * f0 / sqrtOneMinusE2SinPhi2;
232
+ CDBL rho = a * f0 * (L(1.0) - e2) / (oneMinusE2SinPhi2 * sqrtOneMinusE2SinPhi2);
233
+ CDBL eta2 = v / rho - L(1.0);
234
+
235
+ CDBL v2 = v * v;
236
+ CDBL v3 = v2 * v;
237
+ CDBL v5 = v3 * v2;
238
+ CDBL v7 = v5 * v2;
239
+
240
+ CDBL seven = tanPhi / (L(2.0) * rho * v);
241
+ CDBL eight = (tanPhi * (L(5.0) + L(3.0) * tanPhi2 + eta2 - L(9.0) * tanPhi2 * eta2)) / (L(24.0) * rho * v3);
242
+ CDBL nine = (tanPhi * (L(61.0) + L(90.0) * tanPhi2 + L(45.0) * tanPhi4)) / (L(720.0) * rho * v5);
243
+ CDBL ten = secPhi / v;
244
+ CDBL eleven = (secPhi * ((v / rho) + L(2.0) * tanPhi2)) / (L(6.0) * v3);
245
+ CDBL twelve = (secPhi * (L(5.0) + L(28.0) * tanPhi2 + L(24.0) * tanPhi4)) / (L(120.0) * v5);
246
+ CDBL twelveA = (secPhi * (L(61.0) + L(662.0) * tanPhi2 + L(1320.0) * tanPhi4 + L(720.0) * tanPhi6)) / (L(5040.0) * v7);
247
+
248
+ CDBL deltaE = en.e - e0;
249
+ CDBL deltaE2 = deltaE * deltaE;
250
+ CDBL deltaE3 = deltaE2 * deltaE;
251
+ CDBL deltaE4 = deltaE2 * deltaE2;
252
+ CDBL deltaE5 = deltaE3 * deltaE2;
253
+ CDBL deltaE6 = deltaE3 * deltaE3;
254
+ CDBL deltaE7 = deltaE4 * deltaE3;
255
+
256
+ LatLonDecimal latLon;
257
+ latLon.lat = oneEightyOverPi * (phi - seven * deltaE2 + eight * deltaE4 - nine * deltaE6);
258
+ latLon.lon = oneEightyOverPi * (lambda0 + ten * deltaE - eleven * deltaE3 + twelve * deltaE5 - twelveA * deltaE7);
259
+ latLon.elevation = en.elevation;
260
+ latLon.geoid = en.geoid;
261
+
262
+ return latLon;
263
+ }
264
+
265
+ LatLonDecimal ETRS89LatLonFromETRS89EastingNorthing(const EastingNorthing en) {
266
+ return latLonFromEastingNorthing(en, GRS80Ellipsoid, NationalGridProj);
267
+ }
268
+
269
+ EastingNorthing OSTN02ShiftsForIndices(const int eIndex, const int nIndex) {
270
+ EastingNorthing shifts;
271
+ shifts.e = shifts.n = shifts.elevation = shifts.geoid = 0;
272
+ if (nIndex < 0 || nIndex > 1250) return shifts;
273
+
274
+ const OSTN02Index dataIndex = OSTN02Indices[nIndex];
275
+ if (eIndex < dataIndex.eMin || eIndex >= dataIndex.eMin + dataIndex.eCount) return shifts;
276
+
277
+ const unsigned int dataOffset = dataIndex.offset + (eIndex - dataIndex.eMin);
278
+ const OSTN02Datum record = OSTN02Data[dataOffset];
279
+ if (record.gFlag == 0) return shifts;
280
+
281
+ shifts.e = (((DBL) record.eShift) / L(1000.0)) + L(86.0);
282
+ shifts.n = (((DBL) record.nShift) / L(1000.0)) - L(82.0);
283
+ shifts.elevation = (((DBL) record.gShift) / L(1000.0)) + L(43.0);
284
+ shifts.geoid = record.gFlag;
285
+ return shifts;
286
+ }
287
+
288
+ EastingNorthing shiftsForEastingNorthing(const EastingNorthing en) {
289
+ EastingNorthing shifts;
290
+ shifts.e = shifts.n = shifts.elevation = shifts.geoid = 0;
291
+
292
+ const int e0 = (int) (en.e / L(1000.0));
293
+ const int n0 = (int) (en.n / L(1000.0));
294
+ CDBL dx = en.e - (DBL) (e0 * 1000);
295
+ CDBL dy = en.n - (DBL) (n0 * 1000);
296
+ CDBL t = dx / L(1000.0);
297
+ CDBL u = dy / L(1000.0);
298
+
299
+ const EastingNorthing shifts0 = OSTN02ShiftsForIndices(e0 , n0 );
300
+ if (shifts0.geoid == 0) return shifts;
301
+
302
+ const EastingNorthing shifts1 = OSTN02ShiftsForIndices(e0 + 1, n0 );
303
+ if (shifts1.geoid == 0) return shifts;
304
+
305
+ const EastingNorthing shifts2 = OSTN02ShiftsForIndices(e0 + 1, n0 + 1);
306
+ if (shifts2.geoid == 0) return shifts;
307
+
308
+ const EastingNorthing shifts3 = OSTN02ShiftsForIndices(e0 , n0 + 1);
309
+ if (shifts3.geoid == 0) return shifts;
310
+
311
+ CDBL weight0 = (L(1.0) - t) * (L(1.0) - u);
312
+ CDBL weight1 = t * (L(1.0) - u);
313
+ CDBL weight2 = t * u;
314
+ CDBL weight3 = (L(1.0) - t) * u;
315
+
316
+ shifts.e = weight0 * shifts0.e
317
+ + weight1 * shifts1.e
318
+ + weight2 * shifts2.e
319
+ + weight3 * shifts3.e;
320
+
321
+ shifts.n = weight0 * shifts0.n
322
+ + weight1 * shifts1.n
323
+ + weight2 * shifts2.n
324
+ + weight3 * shifts3.n;
325
+
326
+ shifts.elevation = weight0 * shifts0.elevation
327
+ + weight1 * shifts1.elevation
328
+ + weight2 * shifts2.elevation
329
+ + weight3 * shifts3.elevation;
330
+
331
+ const bool left = dx < L(500.0);
332
+ const bool bottom = dy < L(500.0);
333
+ const EastingNorthing nearestShift = left ? (bottom ? shifts0 : shifts3) : (bottom ? shifts1 : shifts2);
334
+ shifts.geoid = nearestShift.geoid;
335
+
336
+ return shifts;
337
+ }
338
+
339
+ EastingNorthing OSGB36EastingNorthingFromETRS89EastingNorthing(const EastingNorthing en) {
340
+ EastingNorthing shifts = shiftsForEastingNorthing(en);
341
+ if (shifts.geoid == 0) return shifts;
342
+
343
+ EastingNorthing shifted;
344
+ shifted.e = en.e + shifts.e;
345
+ shifted.n = en.n + shifts.n;
346
+ shifted.elevation = en.elevation - shifts.elevation;
347
+ shifted.geoid = shifts.geoid;
348
+
349
+ return shifted;
350
+ }
351
+
352
+ EastingNorthing ETRS89EastingNorthingFromOSGB36EastingNorthing(const EastingNorthing en) {
353
+ EastingNorthing shifts, prevShifts, shifted;
354
+ shifts.e = shifts.n = shifted.elevation = shifted.geoid = 0; // initialising .elevation and .geoid just avoids warnings
355
+ do {
356
+ prevShifts.e = shifts.e;
357
+ prevShifts.n = shifts.n;
358
+ shifted.e = en.e - shifts.e;
359
+ shifted.n = en.n - shifts.n;
360
+ shifts = shiftsForEastingNorthing(shifted);
361
+ if (shifts.geoid == 0) return shifts;
362
+ } while (ABS(shifts.e - prevShifts.e) > L(0.0001) || ABS(shifts.n - prevShifts.n) > L(0.0001));
363
+
364
+ shifted.elevation = en.elevation + shifts.elevation;
365
+ shifted.geoid = shifts.geoid; // tells us which geoid datum was used in conversion
366
+ return shifted;
367
+ }
368
+
369
+ char *gridRefFromOSGB36EastingNorthing(const EastingNorthing en, const bool spaces, const int res) {
370
+ // res is expressed in metres: 1/10/100 -> 3/4/5-digit easting and northing
371
+ const int eRound = (int) round(en.e / (DBL) res) * res;
372
+ const int nRound = (int) round(en.n / (DBL) res) * res;
373
+ const int firstEIndex = eRound / 500000;
374
+ const int firstNIndex = nRound / 500000;
375
+ const int secondEIndex = (eRound % 500000) / 100000;
376
+ const int secondNIndex = (nRound % 500000) / 100000;
377
+ const char sq0 = firstLetters[firstNIndex][firstEIndex];
378
+ const char sq1 = secondLetters[secondNIndex][secondEIndex];
379
+ const int e = eRound - (500000 * firstEIndex) - (100000 * secondEIndex);
380
+ const int n = nRound - (500000 * firstNIndex) - (100000 * secondNIndex);
381
+ char *ref, *fmtStr;
382
+ const int digits = res == 100 ? 3 : (res == 10 ? 4 : 5);
383
+ ASPRINTF_OR_DIE(&fmtStr, "%%c%%c%%s%%0%dd%%s%%0%dd", digits, digits);
384
+ ASPRINTF_OR_DIE(&ref, fmtStr, sq0, sq1, (spaces ? " " : ""), e / res, (spaces ? " " : ""), n / res);
385
+ free(fmtStr);
386
+ return ref;
387
+ }
388
+
389
+ char *tetradFromOSGB36EastingNorthing(const EastingNorthing en) {
390
+ // see http://www.bto.org/volunteer-surveys/birdatlas/taking-part/correct-grid-references/know-your-place
391
+ const int eTrunc = (int) en.e; // note: unlike for a grid ref, we never round -- we (implictly) truncate
392
+ const int nTrunc = (int) en.n;
393
+ const int firstEIndex = eTrunc / 500000;
394
+ const int firstNIndex = nTrunc / 500000;
395
+ const int secondEIndex = (eTrunc % 500000) / 100000;
396
+ const int secondNIndex = (nTrunc % 500000) / 100000;
397
+ const char sq0 = firstLetters[firstNIndex][firstEIndex];
398
+ const char sq1 = secondLetters[secondNIndex][secondEIndex];
399
+ const int eMinusSq = eTrunc - (500000 * firstEIndex) - (100000 * secondEIndex);
400
+ const int nMinusSq = nTrunc - (500000 * firstNIndex) - (100000 * secondNIndex);
401
+ const int eDigit = eMinusSq / 10000;
402
+ const int nDigit = nMinusSq / 10000;
403
+ const int tetradEIndex = (eMinusSq % 10000) / 2000;
404
+ const int tetradNIndex = (nMinusSq % 10000) / 2000;
405
+ char tetradLetter = tetradLetters[tetradNIndex][tetradEIndex];
406
+ char *tetrad;
407
+ ASPRINTF_OR_DIE(&tetrad, "%c%c%d%d%c", sq0, sq1, eDigit, nDigit, tetradLetter);
408
+ return tetrad;
409
+ }
410
+
411
+ LatLonDecimal latLonDecimalFromLatLonDegMinSec(const LatLonDegMinSec dms) {
412
+ LatLonDecimal dec;
413
+ dec.lat = (dms.lat.westOrSouth ? L(-1.0) : L(1.0)) * (((DBL) dms.lat.deg) + ((DBL) dms.lat.min) / L(60.0) + dms.lat.sec / L(3600.0));
414
+ dec.lon = (dms.lon.westOrSouth ? L(-1.0) : L(1.0)) * (((DBL) dms.lon.deg) + ((DBL) dms.lon.min) / L(60.0) + dms.lon.sec / L(3600.0));
415
+ dec.elevation = dms.elevation;
416
+ dec.geoid = 0;
417
+ return dec;
418
+ }
419
+
420
+ bool test(const bool noisily) {
421
+ short numTested = 0;
422
+ short numPassed = 0;
423
+ bool testPassed;
424
+
425
+ // check data integrity
426
+
427
+ const unsigned long dataCRC = crc32((unsigned char *) OSTN02Data, sizeof(OSTN02Data));
428
+ testPassed = dataCRC == originalDataCRC;
429
+ numTested ++;
430
+ if (testPassed) numPassed ++;
431
+ if (noisily) {
432
+ printf("\nOriginal CRC32 (data): %li\n", originalDataCRC);
433
+ printf("%sComputed CRC32 (data): %li%s\n\n", (testPassed ? "" : BOLD), dataCRC, (testPassed ? "" : UNBOLD));
434
+ }
435
+
436
+ const unsigned long indicesCRC = crc32((unsigned char *) OSTN02Indices, sizeof(OSTN02Indices));
437
+ testPassed = indicesCRC == originalIndicesCRC;
438
+ numTested ++;
439
+ if (testPassed) numPassed ++;
440
+ if (noisily) {
441
+ printf("Original CRC32 (index): %li\n", originalIndicesCRC);
442
+ printf("%sComputed CRC32 (index): %li%s\n\n", (testPassed ? "" : BOLD), indicesCRC, (testPassed ? "" : UNBOLD));
443
+ }
444
+
445
+ // check test conversions against known good results
446
+
447
+ LatLonDecimal actualLatLon, computedLatLon;
448
+ EastingNorthing actualEN, computedEN;
449
+ char *actualLatLonStr, *computedLatLonStr, *actualENStr, *computedENStr;
450
+
451
+ const int len = LENGTH_OF(testETRSCoords);
452
+ for (int i = 0; i < len; i ++) {
453
+
454
+ // actual coords
455
+
456
+ actualLatLon = latLonDecimalFromLatLonDegMinSec(testETRSCoords[i]);
457
+ actualEN = testOSGB36Coords[i];
458
+
459
+ ASPRINTF_OR_DIE(&actualLatLonStr, LLFMT, actualLatLon.lat, actualLatLon.lon, actualLatLon.elevation);
460
+ ASPRINTF_OR_DIE(&actualENStr, ENFMT, actualEN.e, actualEN.n, actualEN.elevation, OSGB36GeoidRegions[actualEN.geoid], OSGB36GeoidNames[actualEN.geoid]);
461
+
462
+ if (noisily) {
463
+ printf("ETRS89 actual %s\n", actualLatLonStr);
464
+ printf("OSGB36 actual %s\n", actualENStr);
465
+ }
466
+
467
+ // computed coords
468
+
469
+ computedLatLon = ETRS89LatLonFromETRS89EastingNorthing(ETRS89EastingNorthingFromOSGB36EastingNorthing(actualEN));
470
+ computedEN = OSGB36EastingNorthingFromETRS89EastingNorthing(ETRS89EastingNorthingFromETRS89LatLon(actualLatLon));
471
+
472
+ ASPRINTF_OR_DIE(&computedLatLonStr, LLFMT, computedLatLon.lat, computedLatLon.lon, computedLatLon.elevation);
473
+ ASPRINTF_OR_DIE(&computedENStr, ENFMT, computedEN.e, computedEN.n, computedEN.elevation, OSGB36GeoidRegions[computedEN.geoid], OSGB36GeoidNames[computedEN.geoid]);
474
+
475
+ if (actualEN.geoid != 0) { // i.e. these coordinates are not zeroes, and can be converted sensibly
476
+ testPassed = strcmp(actualLatLonStr, computedLatLonStr) == 0;
477
+ numTested ++;
478
+ if (testPassed) numPassed ++;
479
+ if (noisily) printf("%sETRS89 computed %s\n%s", (testPassed ? "" : BOLD), computedLatLonStr, (testPassed ? "" : UNBOLD));
480
+ }
481
+
482
+ testPassed = strcmp(actualENStr, computedENStr) == 0;
483
+ numTested ++;
484
+ if (testPassed) numPassed ++;
485
+ if (noisily) printf("%sOSGB36 computed %s\n\n%s", (testPassed ? "" : BOLD), computedENStr, (testPassed ? "" : UNBOLD));
486
+
487
+ free(actualLatLonStr);
488
+ free(computedLatLonStr);
489
+ free(actualENStr);
490
+ free(computedENStr);
491
+ }
492
+
493
+ /*
494
+ EastingNorthing testEN;
495
+ testEN.e = 651409.903;
496
+ testEN.n = 313177.270;
497
+ printf("Convergence: %13.11lf \n\n", gridConvergenceDegreesFromEastingNorthing(testEN, Airy1830Ellipsoid, NationalGridProj));
498
+ */
499
+
500
+ /*
501
+ LatLonDecimal testLL;
502
+ testLL.lat = 51.0;
503
+ testLL.lon = -2.0;
504
+ printf("Convergence: %13.11lf \n\n", gridConvergenceDegreesFromLatLon(testLL, Airy1830Ellipsoid, NationalGridProj));
505
+ */
506
+
507
+ bool allPassed = numTested == numPassed;
508
+ if (noisily) printf("%i tests; %i passed; %s%i failed%s\n\n", numTested, numPassed, (allPassed ? "" : BOLD), numTested - numPassed, (allPassed ? "" : UNBOLD));
509
+ return allPassed;
510
+ }