source: josm/trunk/src/org/openstreetmap/josm/data/projection/datum/NTV2SubGrid.java@ 6488

Last change on this file since 6488 was 6310, checked in by Don-vip, 11 years ago

Sonar/FindBugs - Nested blocks of code should not be left empty

  • Property svn:eol-style set to native
File size: 12.6 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;
25
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.tools.Utils;
28
29/**
30 * Models the NTv2 Sub Grid within a Grid Shift File
31 *
32 * @author Peter Yuill
33 * Modified for JOSM :
34 * - removed the RandomAccessFile mode (Pieren)
35 * - read grid file by single bytes. Workaround for a bug in some VM not supporting
36 * file reading by group of 4 bytes from a jar file.
37 */
38public class NTV2SubGrid implements Cloneable, Serializable {
39
40 private String subGridName;
41 private String parentSubGridName;
42 private String created;
43 private String updated;
44 private double minLat;
45 private double maxLat;
46 private double minLon;
47 private double maxLon;
48 private double latInterval;
49 private double lonInterval;
50 private int nodeCount;
51
52 private int lonColumnCount;
53 private int latRowCount;
54 private float[] latShift;
55 private float[] lonShift;
56 private float[] latAccuracy;
57 private float[] lonAccuracy;
58
59 boolean bigEndian;
60 private NTV2SubGrid[] subGrid;
61
62 /**
63 * Construct a Sub Grid from an InputStream, loading the node data into
64 * arrays in this object.
65 *
66 * @param in GridShiftFile InputStream
67 * @param bigEndian is the file bigEndian?
68 * @param loadAccuracy is the node Accuracy data to be loaded?
69 * @throws IOException
70 */
71 public NTV2SubGrid(InputStream in, boolean bigEndian, boolean loadAccuracy) throws IOException {
72 byte[] b8 = new byte[8];
73 byte[] b4 = new byte[4];
74 byte[] b1 = new byte[1];
75 in.read(b8);
76 in.read(b8);
77 subGridName = new String(b8).trim();
78 in.read(b8);
79 in.read(b8);
80 parentSubGridName = new String(b8).trim();
81 in.read(b8);
82 in.read(b8);
83 created = new String(b8);
84 in.read(b8);
85 in.read(b8);
86 updated = new String(b8);
87 in.read(b8);
88 in.read(b8);
89 minLat = NTV2Util.getDouble(b8, bigEndian);
90 in.read(b8);
91 in.read(b8);
92 maxLat = NTV2Util.getDouble(b8, bigEndian);
93 in.read(b8);
94 in.read(b8);
95 minLon = NTV2Util.getDouble(b8, bigEndian);
96 in.read(b8);
97 in.read(b8);
98 maxLon = NTV2Util.getDouble(b8, bigEndian);
99 in.read(b8);
100 in.read(b8);
101 latInterval = NTV2Util.getDouble(b8, bigEndian);
102 in.read(b8);
103 in.read(b8);
104 lonInterval = NTV2Util.getDouble(b8, bigEndian);
105 lonColumnCount = 1 + (int)((maxLon - minLon) / lonInterval);
106 latRowCount = 1 + (int)((maxLat - minLat) / latInterval);
107 in.read(b8);
108 in.read(b8);
109 nodeCount = NTV2Util.getInt(b8, bigEndian);
110 if (nodeCount != lonColumnCount * latRowCount)
111 throw new IllegalStateException("SubGrid " + subGridName + " has inconsistent grid dimesions");
112 latShift = new float[nodeCount];
113 lonShift = new float[nodeCount];
114 if (loadAccuracy) {
115 latAccuracy = new float[nodeCount];
116 lonAccuracy = new float[nodeCount];
117 }
118
119 for (int i = 0; i < nodeCount; i++) {
120 // Read the grid file byte after byte. This is a workaround about a bug in
121 // certain VM which are not able to read byte blocks when the resource file is
122 // in a .jar file (Pieren)
123 in.read(b1); b4[0] = b1[0];
124 in.read(b1); b4[1] = b1[0];
125 in.read(b1); b4[2] = b1[0];
126 in.read(b1); b4[3] = b1[0];
127 latShift[i] = NTV2Util.getFloat(b4, bigEndian);
128 in.read(b1); b4[0] = b1[0];
129 in.read(b1); b4[1] = b1[0];
130 in.read(b1); b4[2] = b1[0];
131 in.read(b1); b4[3] = b1[0];
132 lonShift[i] = NTV2Util.getFloat(b4, bigEndian);
133 in.read(b1); b4[0] = b1[0];
134 in.read(b1); b4[1] = b1[0];
135 in.read(b1); b4[2] = b1[0];
136 in.read(b1); b4[3] = b1[0];
137 if (loadAccuracy) {
138 latAccuracy[i] = NTV2Util.getFloat(b4, bigEndian);
139 }
140 in.read(b1); b4[0] = b1[0];
141 in.read(b1); b4[1] = b1[0];
142 in.read(b1); b4[2] = b1[0];
143 in.read(b1); b4[3] = b1[0];
144 if (loadAccuracy) {
145 lonAccuracy[i] = NTV2Util.getFloat(b4, bigEndian);
146 }
147 }
148 }
149
150 /**
151 * Tests if a specified coordinate is within this Sub Grid
152 * or one of its Sub Grids. If the coordinate is outside
153 * this Sub Grid, null is returned. If the coordinate is
154 * within this Sub Grid, but not within any of its Sub Grids,
155 * this Sub Grid is returned. If the coordinate is within
156 * one of this Sub Grid's Sub Grids, the method is called
157 * recursively on the child Sub Grid.
158 *
159 * @param lon Longitude in Positive West Seconds
160 * @param lat Latitude in Seconds
161 * @return the Sub Grid containing the Coordinate or null
162 */
163 public NTV2SubGrid getSubGridForCoord(double lon, double lat) {
164 if (isCoordWithin(lon, lat)) {
165 if (subGrid == null)
166 return this;
167 else {
168 for (NTV2SubGrid aSubGrid : subGrid) {
169 if (aSubGrid.isCoordWithin(lon, lat))
170 return aSubGrid.getSubGridForCoord(lon, lat);
171 }
172 return this;
173 }
174 } else
175 return null;
176 }
177
178 /**
179 * Tests if a specified coordinate is within this Sub Grid.
180 * A coordinate on either outer edge (maximum Latitude or
181 * maximum Longitude) is deemed to be outside the grid.
182 *
183 * @param lon Longitude in Positive West Seconds
184 * @param lat Latitude in Seconds
185 * @return true or false
186 */
187 private boolean isCoordWithin(double lon, double lat) {
188 if ((lon >= minLon) && (lon < maxLon) && (lat >= minLat) && (lat < maxLat))
189 return true;
190 else
191 return false;
192 }
193
194 /**
195 * Bi-Linear interpolation of four nearest node values as described in
196 * 'GDAit Software Architecture Manual' produced by the <a
197 * href='http://www.sli.unimelb.edu.au/gda94'>Geomatics
198 * Department of the University of Melbourne</a>
199 * @param a value at the A node
200 * @param b value at the B node
201 * @param c value at the C node
202 * @param d value at the D node
203 * @param X Longitude factor
204 * @param Y Latitude factor
205 * @return interpolated value
206 */
207 private final double interpolate(float a, float b, float c, float d, double X, double Y) {
208 return a + (((double)b - (double)a) * X) + (((double)c - (double)a) * Y) +
209 (((double)a + (double)d - b - c) * X * Y);
210 }
211
212 /**
213 * Interpolate shift and accuracy values for a coordinate in the 'from' datum
214 * of the GridShiftFile. The algorithm is described in
215 * 'GDAit Software Architecture Manual' produced by the <a
216 * href='http://www.sli.unimelb.edu.au/gda94'>Geomatics
217 * Department of the University of Melbourne</a>
218 * <p>This method is thread safe for both memory based and file based node data.
219 * @param gs GridShift object containing the coordinate to shift and the shift values
220 * @return the GridShift object supplied, with values updated.
221 */
222 public NTV2GridShift interpolateGridShift(NTV2GridShift gs) {
223 int lonIndex = (int)((gs.getLonPositiveWestSeconds() - minLon) / lonInterval);
224 int latIndex = (int)((gs.getLatSeconds() - minLat) / latInterval);
225
226 double X = (gs.getLonPositiveWestSeconds() - (minLon + (lonInterval * lonIndex))) / lonInterval;
227 double Y = (gs.getLatSeconds() - (minLat + (latInterval * latIndex))) / latInterval;
228
229 // Find the nodes at the four corners of the cell
230
231 int indexA = lonIndex + (latIndex * lonColumnCount);
232 int indexB = indexA + 1;
233 int indexC = indexA + lonColumnCount;
234 int indexD = indexC + 1;
235
236 gs.setLonShiftPositiveWestSeconds(interpolate(
237 lonShift[indexA], lonShift[indexB], lonShift[indexC], lonShift[indexD], X, Y));
238
239 gs.setLatShiftSeconds(interpolate(
240 latShift[indexA], latShift[indexB], latShift[indexC], latShift[indexD], X, Y));
241
242 if (lonAccuracy == null) {
243 gs.setLonAccuracyAvailable(false);
244 } else {
245 gs.setLonAccuracyAvailable(true);
246 gs.setLonAccuracySeconds(interpolate(
247 lonAccuracy[indexA], lonAccuracy[indexB], lonAccuracy[indexC], lonAccuracy[indexD], X, Y));
248 }
249
250 if (latAccuracy == null) {
251 gs.setLatAccuracyAvailable(false);
252 } else {
253 gs.setLatAccuracyAvailable(true);
254 gs.setLatAccuracySeconds(interpolate(
255 latAccuracy[indexA], latAccuracy[indexB], latAccuracy[indexC], latAccuracy[indexD], X, Y));
256 }
257 return gs;
258 }
259
260 public String getParentSubGridName() {
261 return parentSubGridName;
262 }
263
264 public String getSubGridName() {
265 return subGridName;
266 }
267
268 public int getNodeCount() {
269 return nodeCount;
270 }
271
272 public int getSubGridCount() {
273 return (subGrid == null) ? 0 : subGrid.length;
274 }
275
276 public NTV2SubGrid getSubGrid(int index) {
277 return (subGrid == null) ? null : subGrid[index];
278 }
279
280 /**
281 * Set an array of Sub Grids of this sub grid
282 * @param subGrid
283 */
284 public void setSubGridArray(NTV2SubGrid[] subGrid) {
285 this.subGrid = Utils.copyArray(subGrid);
286 }
287
288 @Override
289 public String toString() {
290 return subGridName;
291 }
292
293 /**
294 * Returns textual details about the sub grid.
295 * @return textual details about the sub grid
296 */
297 public String getDetails() {
298 StringBuffer buf = new StringBuffer("Sub Grid : ");
299 buf.append(subGridName);
300 buf.append("\nParent : ");
301 buf.append(parentSubGridName);
302 buf.append("\nCreated : ");
303 buf.append(created);
304 buf.append("\nUpdated : ");
305 buf.append(updated);
306 buf.append("\nMin Lat : ");
307 buf.append(minLat);
308 buf.append("\nMax Lat : ");
309 buf.append(maxLat);
310 buf.append("\nMin Lon : ");
311 buf.append(minLon);
312 buf.append("\nMax Lon : ");
313 buf.append(maxLon);
314 buf.append("\nLat Intvl: ");
315 buf.append(latInterval);
316 buf.append("\nLon Intvl: ");
317 buf.append(lonInterval);
318 buf.append("\nNode Cnt : ");
319 buf.append(nodeCount);
320 return buf.toString();
321 }
322
323 /**
324 * Make a deep clone of this Sub Grid
325 */
326 @Override
327 public Object clone() {
328 NTV2SubGrid clone = null;
329 try {
330 clone = (NTV2SubGrid)super.clone();
331 // Do a deep clone of the sub grids
332 if (subGrid != null) {
333 clone.subGrid = new NTV2SubGrid[subGrid.length];
334 for (int i = 0; i < subGrid.length; i++) {
335 clone.subGrid[i] = (NTV2SubGrid)subGrid[i].clone();
336 }
337 }
338 } catch (CloneNotSupportedException cnse) {
339 Main.warn(cnse);
340 }
341 return clone;
342 }
343 /**
344 * Get maximum latitude value
345 * @return maximum latitude
346 */
347 public double getMaxLat() {
348 return maxLat;
349 }
350
351 /**
352 * Get maximum longitude value
353 * @return maximum longitude
354 */
355 public double getMaxLon() {
356 return maxLon;
357 }
358
359 /**
360 * Get minimum latitude value
361 * @return minimum latitude
362 */
363 public double getMinLat() {
364 return minLat;
365 }
366
367 /**
368 * Get minimum longitude value
369 * @return minimum longitude
370 */
371 public double getMinLon() {
372 return minLon;
373 }
374}
Note: See TracBrowser for help on using the repository browser.