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