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

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

javadoc fixes for jdk8 compatibility

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