/* * Copyright (c) 2003 Objectix Pty Ltd All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL OBJECTIX PTY LTD BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.openstreetmap.josm.data.projection.datum; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openstreetmap.josm.tools.Logging; /** * Models the NTv2 format Grid Shift File and exposes methods to shift * coordinate values using the Sub Grids contained in the file. *
The principal reference for the algorithms used is the * 'GDAit Software Architecture Manual' produced by the Geomatics * Department of the University of Melbourne *
This library reads binary NTv2 Grid Shift files in Big Endian * (Canadian standard) or Little Endian (Australian Standard) format. * The older 'Australian' binary format is not supported, only the * official Canadian format, which is now also used for the national * Australian Grid. *
Grid Shift files can be read as InputStreams or RandomAccessFiles. * Loading an InputStream places all the required node information * (accuracy data is optional) into heap based Java arrays. This is the * highest perfomance option, and is useful for large volume transformations. * Non-file data sources (eg using an SQL Blob) are also supported through * InputStream. The RandonAccessFile option has a much smaller memory * footprint as only the Sub Grid headers are stored in memory, but * transformation is slower because the file must be read a number of * times for each transformation. *
Coordinates may be shifted Forward (ie from and to the Datums specified * in the Grid Shift File header) or Reverse. The reverse transformation * uses an iterative approach to approximate the Grid Shift, as the * precise transformation is based on 'from' datum coordinates. *
Coordinates may be specified * either in Seconds using Positive West Longitude (the original NTv2 * arrangement) or in decimal Degrees using Positive East Longitude. * * @author Peter Yuill * Modified for JOSM : * - removed the RandomAccessFile mode (Pieren) * @since 2507 */ public class NTV2GridShiftFile implements Serializable { private static final long serialVersionUID = 1L; private int overviewHeaderCount; private int subGridHeaderCount; private int subGridCount; private String shiftType; private String version; private String fromEllipsoid = ""; private String toEllipsoid = ""; private double fromSemiMajorAxis; private double fromSemiMinorAxis; private double toSemiMajorAxis; private double toSemiMinorAxis; private NTV2SubGrid[] topLevelSubGrid; private NTV2SubGrid lastSubGrid; private static void readBytes(InputStream in, byte[] b) throws IOException { if (in.read(b) < b.length) { Logging.error("Failed to read expected amount of bytes ("+ b.length +") from stream"); } } /** * Load a Grid Shift File from an InputStream. The Grid Shift node * data is stored in Java arrays, which will occupy about the same memory * as the original file with accuracy data included, and about half that * with accuracy data excluded. The size of the Australian national file * is 4.5MB, and the Canadian national file is 13.5MB *
The InputStream is closed by this method.
*
* @param in Grid Shift File InputStream
* @param loadAccuracy is Accuracy data to be loaded as well as shift data?
* @throws IOException if any I/O error occurs
*/
public void loadGridShiftFile(InputStream in, boolean loadAccuracy) throws IOException {
byte[] b8 = new byte[8];
fromEllipsoid = "";
toEllipsoid = "";
topLevelSubGrid = null;
readBytes(in, b8);
String overviewHeaderCountId = new String(b8, StandardCharsets.UTF_8);
if (!"NUM_OREC".equals(overviewHeaderCountId))
throw new IllegalArgumentException("Input file is not an NTv2 grid shift file");
boolean bigEndian;
readBytes(in, b8);
overviewHeaderCount = NTV2Util.getIntBE(b8, 0);
if (overviewHeaderCount == 11) {
bigEndian = true;
} else {
overviewHeaderCount = NTV2Util.getIntLE(b8, 0);
if (overviewHeaderCount == 11) {
bigEndian = false;
} else
throw new IllegalArgumentException("Input file is not an NTv2 grid shift file");
}
readBytes(in, b8);
readBytes(in, b8);
subGridHeaderCount = NTV2Util.getInt(b8, bigEndian);
readBytes(in, b8);
readBytes(in, b8);
subGridCount = NTV2Util.getInt(b8, bigEndian);
NTV2SubGrid[] subGrid = new NTV2SubGrid[subGridCount];
readBytes(in, b8);
readBytes(in, b8);
shiftType = new String(b8, StandardCharsets.UTF_8);
readBytes(in, b8);
readBytes(in, b8);
version = new String(b8, StandardCharsets.UTF_8);
readBytes(in, b8);
readBytes(in, b8);
fromEllipsoid = new String(b8, StandardCharsets.UTF_8);
readBytes(in, b8);
readBytes(in, b8);
toEllipsoid = new String(b8, StandardCharsets.UTF_8);
readBytes(in, b8);
readBytes(in, b8);
fromSemiMajorAxis = NTV2Util.getDouble(b8, bigEndian);
readBytes(in, b8);
readBytes(in, b8);
fromSemiMinorAxis = NTV2Util.getDouble(b8, bigEndian);
readBytes(in, b8);
readBytes(in, b8);
toSemiMajorAxis = NTV2Util.getDouble(b8, bigEndian);
readBytes(in, b8);
readBytes(in, b8);
toSemiMinorAxis = NTV2Util.getDouble(b8, bigEndian);
for (int i = 0; i < subGridCount; i++) {
subGrid[i] = new NTV2SubGrid(in, bigEndian, loadAccuracy);
}
topLevelSubGrid = createSubGridTree(subGrid);
lastSubGrid = topLevelSubGrid[0];
}
/**
* Create a tree of Sub Grids by adding each Sub Grid to its parent (where
* it has one), and returning an array of the top level Sub Grids
* @param subGrid an array of all Sub Grids
* @return an array of top level Sub Grids with lower level Sub Grids set.
*/
private static NTV2SubGrid[] createSubGridTree(NTV2SubGrid... subGrid) {
int topLevelCount = 0;
Map