source: josm/trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2GridShiftFile.java@ 8308

Last change on this file since 8308 was 8308, checked in by Don-vip, 9 years ago

fix potential NPEs and Sonar issues related to serialization

  • Property svn:eol-style set to native
File size: 12.5 KB
Line 
1/*
2 * Copyright (c) 2003 Objectix Pty Ltd All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation.
7 *
8 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
9 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
10 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11 * DISCLAIMED. IN NO EVENT SHALL OBJECTIX PTY LTD BE LIABLE FOR ANY
12 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
14 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
15 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
16 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
17 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
18 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19 */
20package org.openstreetmap.josm.data.projection.datum;
21
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.Serializable;
25import java.nio.charset.StandardCharsets;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.List;
29
30/**
31 * Models the NTv2 format Grid Shift File and exposes methods to shift
32 * coordinate values using the Sub Grids contained in the file.
33 * <p>The principal reference for the alogrithms used is the
34 * 'GDAit Software Architecture Manual' produced by the <a
35 * href='http://www.sli.unimelb.edu.au/gda94'>Geomatics
36 * Department of the University of Melbourne</a>
37 * <p>This library reads binary NTv2 Grid Shift files in Big Endian
38 * (Canadian standard) or Little Endian (Australian Standard) format.
39 * The older 'Australian' binary format is not supported, only the
40 * official Canadian format, which is now also used for the national
41 * Australian Grid.
42 * <p>Grid Shift files can be read as InputStreams or RandomAccessFiles.
43 * Loading an InputStream places all the required node information
44 * (accuracy data is optional) into heap based Java arrays. This is the
45 * highest perfomance option, and is useful for large volume transformations.
46 * Non-file data sources (eg using an SQL Blob) are also supported through
47 * InputStream. The RandonAccessFile option has a much smaller memory
48 * footprint as only the Sub Grid headers are stored in memory, but
49 * transformation is slower because the file must be read a number of
50 * times for each transformation.
51 * <p>Coordinates may be shifted Forward (ie from and to the Datums specified
52 * in the Grid Shift File header) or Reverse. The reverse transformation
53 * uses an iterative approach to approximate the Grid Shift, as the
54 * precise transformation is based on 'from' datum coordinates.
55 * <p>Coordinates may be specified
56 * either in Seconds using Positive West Longitude (the original NTv2
57 * arrangement) or in decimal Degrees using Positive East Longitude.
58 *
59 * @author Peter Yuill
60 * Modifified for JOSM :
61 * - removed the RandomAccessFile mode (Pieren)
62 */
63public class NTV2GridShiftFile implements Serializable {
64
65 private static final long serialVersionUID = 1L;
66
67 private int overviewHeaderCount;
68 private int subGridHeaderCount;
69 private int subGridCount;
70 private String shiftType;
71 private String version;
72 private String fromEllipsoid = "";
73 private String toEllipsoid = "";
74 private double fromSemiMajorAxis;
75 private double fromSemiMinorAxis;
76 private double toSemiMajorAxis;
77 private double toSemiMinorAxis;
78
79 private NTV2SubGrid[] topLevelSubGrid;
80 private NTV2SubGrid lastSubGrid;
81
82 /**
83 * Constructs a new {@code NTV2GridShiftFile}.
84 */
85 public NTV2GridShiftFile() {
86 }
87
88 /**
89 * Load a Grid Shift File from an InputStream. The Grid Shift node
90 * data is stored in Java arrays, which will occupy about the same memory
91 * as the original file with accuracy data included, and about half that
92 * with accuracy data excluded. The size of the Australian national file
93 * is 4.5MB, and the Canadian national file is 13.5MB
94 * <p>The InputStream is closed by this method.
95 *
96 * @param in Grid Shift File InputStream
97 * @param loadAccuracy is Accuracy data to be loaded as well as shift data?
98 * @throws IOException
99 */
100 public void loadGridShiftFile(InputStream in, boolean loadAccuracy ) throws IOException {
101 byte[] b8 = new byte[8];
102 boolean bigEndian = true;
103 fromEllipsoid = "";
104 toEllipsoid = "";
105 topLevelSubGrid = null;
106 in.read(b8);
107 String overviewHeaderCountId = new String(b8, StandardCharsets.UTF_8);
108 if (!"NUM_OREC".equals(overviewHeaderCountId))
109 throw new IllegalArgumentException("Input file is not an NTv2 grid shift file");
110 in.read(b8);
111 overviewHeaderCount = NTV2Util.getIntBE(b8, 0);
112 if (overviewHeaderCount == 11) {
113 bigEndian = true;
114 } else {
115 overviewHeaderCount = NTV2Util.getIntLE(b8, 0);
116 if (overviewHeaderCount == 11) {
117 bigEndian = false;
118 } else
119 throw new IllegalArgumentException("Input file is not an NTv2 grid shift file");
120 }
121 in.read(b8);
122 in.read(b8);
123 subGridHeaderCount = NTV2Util.getInt(b8, bigEndian);
124 in.read(b8);
125 in.read(b8);
126 subGridCount = NTV2Util.getInt(b8, bigEndian);
127 NTV2SubGrid[] subGrid = new NTV2SubGrid[subGridCount];
128 in.read(b8);
129 in.read(b8);
130 shiftType = new String(b8, StandardCharsets.UTF_8);
131 in.read(b8);
132 in.read(b8);
133 version = new String(b8, StandardCharsets.UTF_8);
134 in.read(b8);
135 in.read(b8);
136 fromEllipsoid = new String(b8, StandardCharsets.UTF_8);
137 in.read(b8);
138 in.read(b8);
139 toEllipsoid = new String(b8, StandardCharsets.UTF_8);
140 in.read(b8);
141 in.read(b8);
142 fromSemiMajorAxis = NTV2Util.getDouble(b8, bigEndian);
143 in.read(b8);
144 in.read(b8);
145 fromSemiMinorAxis = NTV2Util.getDouble(b8, bigEndian);
146 in.read(b8);
147 in.read(b8);
148 toSemiMajorAxis = NTV2Util.getDouble(b8, bigEndian);
149 in.read(b8);
150 in.read(b8);
151 toSemiMinorAxis = NTV2Util.getDouble(b8, bigEndian);
152
153 for (int i = 0; i < subGridCount; i++) {
154 subGrid[i] = new NTV2SubGrid(in, bigEndian, loadAccuracy);
155 }
156 topLevelSubGrid = createSubGridTree(subGrid);
157 lastSubGrid = topLevelSubGrid[0];
158 }
159
160 /**
161 * Create a tree of Sub Grids by adding each Sub Grid to its parent (where
162 * it has one), and returning an array of the top level Sub Grids
163 * @param subGrid an array of all Sub Grids
164 * @return an array of top level Sub Grids with lower level Sub Grids set.
165 */
166 private NTV2SubGrid[] createSubGridTree(NTV2SubGrid[] subGrid) {
167 int topLevelCount = 0;
168 HashMap<String, List<NTV2SubGrid>> subGridMap = new HashMap<>();
169 for (int i = 0; i < subGrid.length; i++) {
170 if ("NONE".equalsIgnoreCase(subGrid[i].getParentSubGridName())) {
171 topLevelCount++;
172 }
173 subGridMap.put(subGrid[i].getSubGridName(), new ArrayList<NTV2SubGrid>());
174 }
175 NTV2SubGrid[] topLevelSubGrid = new NTV2SubGrid[topLevelCount];
176 topLevelCount = 0;
177 for (int i = 0; i < subGrid.length; i++) {
178 if ("NONE".equalsIgnoreCase(subGrid[i].getParentSubGridName())) {
179 topLevelSubGrid[topLevelCount++] = subGrid[i];
180 } else {
181 List<NTV2SubGrid> parent = subGridMap.get(subGrid[i].getParentSubGridName());
182 parent.add(subGrid[i]);
183 }
184 }
185 NTV2SubGrid[] nullArray = new NTV2SubGrid[0];
186 for (int i = 0; i < subGrid.length; i++) {
187 List<NTV2SubGrid> subSubGrids = subGridMap.get(subGrid[i].getSubGridName());
188 if (!subSubGrids.isEmpty()) {
189 NTV2SubGrid[] subGridArray = subSubGrids.toArray(nullArray);
190 subGrid[i].setSubGridArray(subGridArray);
191 }
192 }
193 return topLevelSubGrid;
194 }
195
196 /**
197 * Shift a coordinate in the Forward direction of the Grid Shift File.
198 *
199 * @param gs A GridShift object containing the coordinate to shift
200 * @return True if the coordinate is within a Sub Grid, false if not
201 */
202 public boolean gridShiftForward(NTV2GridShift gs) {
203 // Try the last sub grid first, big chance the coord is still within it
204 NTV2SubGrid subGrid = lastSubGrid.getSubGridForCoord(gs.getLonPositiveWestSeconds(), gs.getLatSeconds());
205 if (subGrid == null) {
206 subGrid = getSubGrid(gs.getLonPositiveWestSeconds(), gs.getLatSeconds());
207 }
208 if (subGrid == null)
209 return false;
210 else {
211 subGrid.interpolateGridShift(gs);
212 gs.setSubGridName(subGrid.getSubGridName());
213 lastSubGrid = subGrid;
214 return true;
215 }
216 }
217
218 /**
219 * Shift a coordinate in the Reverse direction of the Grid Shift File.
220 *
221 * @param gs A GridShift object containing the coordinate to shift
222 * @return True if the coordinate is within a Sub Grid, false if not
223 */
224 public boolean gridShiftReverse(NTV2GridShift gs) {
225 // set up the first estimate
226 NTV2GridShift forwardGs = new NTV2GridShift();
227 forwardGs.setLonPositiveWestSeconds(gs.getLonPositiveWestSeconds());
228 forwardGs.setLatSeconds(gs.getLatSeconds());
229 for (int i = 0; i < 4; i++) {
230 if (!gridShiftForward(forwardGs))
231 return false;
232 forwardGs.setLonPositiveWestSeconds(
233 gs.getLonPositiveWestSeconds() - forwardGs.getLonShiftPositiveWestSeconds());
234 forwardGs.setLatSeconds(gs.getLatSeconds() - forwardGs.getLatShiftSeconds());
235 }
236 gs.setLonShiftPositiveWestSeconds(-forwardGs.getLonShiftPositiveWestSeconds());
237 gs.setLatShiftSeconds(-forwardGs.getLatShiftSeconds());
238 gs.setLonAccuracyAvailable(forwardGs.isLonAccuracyAvailable());
239 if (forwardGs.isLonAccuracyAvailable()) {
240 gs.setLonAccuracySeconds(forwardGs.getLonAccuracySeconds());
241 }
242 gs.setLatAccuracyAvailable(forwardGs.isLatAccuracyAvailable());
243 if (forwardGs.isLatAccuracyAvailable()) {
244 gs.setLatAccuracySeconds(forwardGs.getLatAccuracySeconds());
245 }
246 return true;
247 }
248
249 /**
250 * Find the finest SubGrid containing the coordinate, specified
251 * in Positive West Seconds
252 *
253 * @param lon Longitude in Positive West Seconds
254 * @param lat Latitude in Seconds
255 * @return The SubGrid found or null
256 */
257 private NTV2SubGrid getSubGrid(double lon, double lat) {
258 NTV2SubGrid sub = null;
259 for (int i = 0; i < topLevelSubGrid.length; i++) {
260 sub = topLevelSubGrid[i].getSubGridForCoord(lon, lat);
261 if (sub != null) {
262 break;
263 }
264 }
265 return sub;
266 }
267
268 public boolean isLoaded() {
269 return (topLevelSubGrid != null);
270 }
271
272 public void unload() {
273 topLevelSubGrid = null;
274 }
275
276 @Override
277 public String toString() {
278 StringBuilder buf = new StringBuilder("Headers : ");
279 buf.append(overviewHeaderCount);
280 buf.append("\nSub Hdrs : ");
281 buf.append(subGridHeaderCount);
282 buf.append("\nSub Grids: ");
283 buf.append(subGridCount);
284 buf.append("\nType : ");
285 buf.append(shiftType);
286 buf.append("\nVersion : ");
287 buf.append(version);
288 buf.append("\nFr Ellpsd: ");
289 buf.append(fromEllipsoid);
290 buf.append("\nTo Ellpsd: ");
291 buf.append(toEllipsoid);
292 buf.append("\nFr Maj Ax: ");
293 buf.append(fromSemiMajorAxis);
294 buf.append("\nFr Min Ax: ");
295 buf.append(fromSemiMinorAxis);
296 buf.append("\nTo Maj Ax: ");
297 buf.append(toSemiMajorAxis);
298 buf.append("\nTo Min Ax: ");
299 buf.append(toSemiMinorAxis);
300 return buf.toString();
301 }
302
303 /**
304 * Get a copy of the SubGrid tree for this file.
305 *
306 * @return a deep clone of the current SubGrid tree
307 */
308 public NTV2SubGrid[] getSubGridTree() {
309 NTV2SubGrid[] clone = new NTV2SubGrid[topLevelSubGrid.length];
310 for (int i = 0; i < topLevelSubGrid.length; i++) {
311 clone[i] = (NTV2SubGrid)topLevelSubGrid[i].clone();
312 }
313 return clone;
314 }
315
316 public String getFromEllipsoid() {
317 return fromEllipsoid;
318 }
319
320 public String getToEllipsoid() {
321 return toEllipsoid;
322 }
323
324}
Note: See TracBrowser for help on using the repository browser.