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

Last change on this file since 4225 was 3083, checked in by bastiK, 14 years ago

added svn:eol-style=native to source files

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