national_grid 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }