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

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

sonar - squid:S2325 - "private" methods that don't access instance data should be "static"

  • Property svn:eol-style set to native
File size: 13.2 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 if any I/O error occurs
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 readBytes(in, b8);
78 readBytes(in, b8);
79 subGridName = new String(b8, StandardCharsets.UTF_8).trim();
80 readBytes(in, b8);
81 readBytes(in, b8);
82 parentSubGridName = new String(b8, StandardCharsets.UTF_8).trim();
83 readBytes(in, b8);
84 readBytes(in, b8);
85 created = new String(b8, StandardCharsets.UTF_8);
86 readBytes(in, b8);
87 readBytes(in, b8);
88 updated = new String(b8, StandardCharsets.UTF_8);
89 readBytes(in, b8);
90 readBytes(in, b8);
91 minLat = NTV2Util.getDouble(b8, bigEndian);
92 readBytes(in, b8);
93 readBytes(in, b8);
94 maxLat = NTV2Util.getDouble(b8, bigEndian);
95 readBytes(in, b8);
96 readBytes(in, b8);
97 minLon = NTV2Util.getDouble(b8, bigEndian);
98 readBytes(in, b8);
99 readBytes(in, b8);
100 maxLon = NTV2Util.getDouble(b8, bigEndian);
101 readBytes(in, b8);
102 readBytes(in, b8);
103 latInterval = NTV2Util.getDouble(b8, bigEndian);
104 readBytes(in, b8);
105 readBytes(in, b8);
106 lonInterval = NTV2Util.getDouble(b8, bigEndian);
107 lonColumnCount = 1 + (int) ((maxLon - minLon) / lonInterval);
108 latRowCount = 1 + (int) ((maxLat - minLat) / latInterval);
109 readBytes(in, b8);
110 readBytes(in, 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 in a .jar file (Pieren)
124 readBytes(in, b1); b4[0] = b1[0];
125 readBytes(in, b1); b4[1] = b1[0];
126 readBytes(in, b1); b4[2] = b1[0];
127 readBytes(in, b1); b4[3] = b1[0];
128 latShift[i] = NTV2Util.getFloat(b4, bigEndian);
129 readBytes(in, b1); b4[0] = b1[0];
130 readBytes(in, b1); b4[1] = b1[0];
131 readBytes(in, b1); b4[2] = b1[0];
132 readBytes(in, b1); b4[3] = b1[0];
133 lonShift[i] = NTV2Util.getFloat(b4, bigEndian);
134 readBytes(in, b1); b4[0] = b1[0];
135 readBytes(in, b1); b4[1] = b1[0];
136 readBytes(in, b1); b4[2] = b1[0];
137 readBytes(in, b1); b4[3] = b1[0];
138 if (loadAccuracy) {
139 latAccuracy[i] = NTV2Util.getFloat(b4, bigEndian);
140 }
141 readBytes(in, b1); b4[0] = b1[0];
142 readBytes(in, b1); b4[1] = b1[0];
143 readBytes(in, b1); b4[2] = b1[0];
144 readBytes(in, b1); b4[3] = b1[0];
145 if (loadAccuracy) {
146 lonAccuracy[i] = NTV2Util.getFloat(b4, bigEndian);
147 }
148 }
149 }
150
151 private static void readBytes(InputStream in, byte[] b) throws IOException {
152 if (in.read(b) < b.length) {
153 Main.error("Failed to read expected amount of bytes ("+ b.length +") from stream");
154 }
155 }
156
157 /**
158 * Tests if a specified coordinate is within this Sub Grid
159 * or one of its Sub Grids. If the coordinate is outside
160 * this Sub Grid, null is returned. If the coordinate is
161 * within this Sub Grid, but not within any of its Sub Grids,
162 * this Sub Grid is returned. If the coordinate is within
163 * one of this Sub Grid's Sub Grids, the method is called
164 * recursively on the child Sub Grid.
165 *
166 * @param lon Longitude in Positive West Seconds
167 * @param lat Latitude in Seconds
168 * @return the Sub Grid containing the Coordinate or null
169 */
170 public NTV2SubGrid getSubGridForCoord(double lon, double lat) {
171 if (isCoordWithin(lon, lat)) {
172 if (subGrid == null)
173 return this;
174 else {
175 for (NTV2SubGrid aSubGrid : subGrid) {
176 if (aSubGrid.isCoordWithin(lon, lat))
177 return aSubGrid.getSubGridForCoord(lon, lat);
178 }
179 return this;
180 }
181 } else
182 return null;
183 }
184
185 /**
186 * Tests if a specified coordinate is within this Sub Grid.
187 * A coordinate on either outer edge (maximum Latitude or
188 * maximum Longitude) is deemed to be outside the grid.
189 *
190 * @param lon Longitude in Positive West Seconds
191 * @param lat Latitude in Seconds
192 * @return true or false
193 */
194 private boolean isCoordWithin(double lon, double lat) {
195 return (lon >= minLon) && (lon < maxLon) && (lat >= minLat) && (lat < maxLat);
196 }
197
198 /**
199 * Bi-Linear interpolation of four nearest node values as described in
200 * 'GDAit Software Architecture Manual' produced by the <a
201 * href='http://www.dtpli.vic.gov.au/property-and-land-titles/geodesy/geocentric-datum-of-australia-1994-gda94/gda94-useful-tools'>
202 * Geomatics Department of the University of Melbourne</a>
203 * @param a value at the A node
204 * @param b value at the B node
205 * @param c value at the C node
206 * @param d value at the D node
207 * @param x Longitude factor
208 * @param y Latitude factor
209 * @return interpolated value
210 */
211 private static double interpolate(float a, float b, float c, float d, double x, double y) {
212 return a + (((double) b - (double) a) * x) + (((double) c - (double) a) * y) +
213 (((double) a + (double) d - b - c) * x * y);
214 }
215
216 /**
217 * Interpolate shift and accuracy values for a coordinate in the 'from' datum
218 * of the GridShiftFile. The algorithm is described in
219 * 'GDAit Software Architecture Manual' produced by the <a
220 * href='http://www.dtpli.vic.gov.au/property-and-land-titles/geodesy/geocentric-datum-of-australia-1994-gda94/gda94-useful-tools'>
221 * Geomatics Department of the University of Melbourne</a>
222 * <p>This method is thread safe for both memory based and file based node data.
223 * @param gs GridShift object containing the coordinate to shift and the shift values
224 */
225 public void interpolateGridShift(NTV2GridShift gs) {
226 int lonIndex = (int) ((gs.getLonPositiveWestSeconds() - minLon) / lonInterval);
227 int latIndex = (int) ((gs.getLatSeconds() - minLat) / latInterval);
228
229 double x = (gs.getLonPositiveWestSeconds() - (minLon + (lonInterval * lonIndex))) / lonInterval;
230 double y = (gs.getLatSeconds() - (minLat + (latInterval * latIndex))) / latInterval;
231
232 // Find the nodes at the four corners of the cell
233
234 int indexA = lonIndex + (latIndex * lonColumnCount);
235 int indexB = indexA + 1;
236 int indexC = indexA + lonColumnCount;
237 int indexD = indexC + 1;
238
239 gs.setLonShiftPositiveWestSeconds(interpolate(
240 lonShift[indexA], lonShift[indexB], lonShift[indexC], lonShift[indexD], x, y));
241
242 gs.setLatShiftSeconds(interpolate(
243 latShift[indexA], latShift[indexB], latShift[indexC], latShift[indexD], x, y));
244
245 if (lonAccuracy == null) {
246 gs.setLonAccuracyAvailable(false);
247 } else {
248 gs.setLonAccuracyAvailable(true);
249 gs.setLonAccuracySeconds(interpolate(
250 lonAccuracy[indexA], lonAccuracy[indexB], lonAccuracy[indexC], lonAccuracy[indexD], x, y));
251 }
252
253 if (latAccuracy == null) {
254 gs.setLatAccuracyAvailable(false);
255 } else {
256 gs.setLatAccuracyAvailable(true);
257 gs.setLatAccuracySeconds(interpolate(
258 latAccuracy[indexA], latAccuracy[indexB], latAccuracy[indexC], latAccuracy[indexD], x, y));
259 }
260 }
261
262 public String getParentSubGridName() {
263 return parentSubGridName;
264 }
265
266 public String getSubGridName() {
267 return subGridName;
268 }
269
270 public int getNodeCount() {
271 return nodeCount;
272 }
273
274 public int getSubGridCount() {
275 return (subGrid == null) ? 0 : subGrid.length;
276 }
277
278 public NTV2SubGrid getSubGrid(int index) {
279 return (subGrid == null) ? null : subGrid[index];
280 }
281
282 /**
283 * Set an array of Sub Grids of this sub grid
284 * @param subGrid subgrids
285 */
286 public void setSubGridArray(NTV2SubGrid[] subGrid) {
287 this.subGrid = Utils.copyArray(subGrid);
288 }
289
290 @Override
291 public String toString() {
292 return subGridName;
293 }
294
295 /**
296 * Returns textual details about the sub grid.
297 * @return textual details about the sub grid
298 */
299 public String getDetails() {
300 StringBuilder buff = new StringBuilder("Sub Grid : ");
301 buff.append(subGridName)
302 .append("\nParent : ")
303 .append(parentSubGridName)
304 .append("\nCreated : ")
305 .append(created)
306 .append("\nUpdated : ")
307 .append(updated)
308 .append("\nMin Lat : ")
309 .append(minLat)
310 .append("\nMax Lat : ")
311 .append(maxLat)
312 .append("\nMin Lon : ")
313 .append(minLon)
314 .append("\nMax Lon : ")
315 .append(maxLon)
316 .append("\nLat Intvl: ")
317 .append(latInterval)
318 .append("\nLon Intvl: ")
319 .append(lonInterval)
320 .append("\nNode Cnt : ")
321 .append(nodeCount);
322 return buff.toString();
323 }
324
325 /**
326 * Make a deep clone of this Sub Grid
327 */
328 @Override
329 public Object clone() {
330 NTV2SubGrid clone = null;
331 try {
332 clone = (NTV2SubGrid) super.clone();
333 // Do a deep clone of the sub grids
334 if (subGrid != null) {
335 clone.subGrid = new NTV2SubGrid[subGrid.length];
336 for (int i = 0; i < subGrid.length; i++) {
337 clone.subGrid[i] = (NTV2SubGrid) subGrid[i].clone();
338 }
339 }
340 } catch (CloneNotSupportedException cnse) {
341 Main.warn(cnse);
342 }
343 return clone;
344 }
345
346 /**
347 * Get maximum latitude value
348 * @return maximum latitude
349 */
350 public double getMaxLat() {
351 return maxLat;
352 }
353
354 /**
355 * Get maximum longitude value
356 * @return maximum longitude
357 */
358 public double getMaxLon() {
359 return maxLon;
360 }
361
362 /**
363 * Get minimum latitude value
364 * @return minimum latitude
365 */
366 public double getMinLat() {
367 return minLat;
368 }
369
370 /**
371 * Get minimum longitude value
372 * @return minimum longitude
373 */
374 public double getMinLon() {
375 return minLon;
376 }
377}
Note: See TracBrowser for help on using the repository browser.