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

Last change on this file since 14159 was 13627, checked in by Don-vip, 6 years ago

see #16129 - datum javadoc

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