/* * Copyright (c) 2016 Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS 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 METRON, INC. 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 com.metsci.glimpse.charts.bathy; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Comparator; import java.util.NavigableSet; import java.util.TreeSet; import com.metsci.glimpse.axis.Axis2D; import com.metsci.glimpse.support.projection.LatLonProjection; import com.metsci.glimpse.support.projection.Projection; import com.metsci.glimpse.support.texture.FloatTextureProjected2D; import com.metsci.glimpse.util.geo.LatLonGeo; import com.metsci.glimpse.util.geo.projection.GeoProjection; import com.metsci.glimpse.util.units.Angle; import com.metsci.glimpse.util.vector.Vector2d; /** * @author ulman */ public class BathymetryData { protected double widthStep; protected double heightStep; protected double startLon; protected double startLat; protected int imageHeight; protected int imageWidth; protected GeoProjection projection; protected double[][] data; public BathymetryData( InputStream in, GeoProjection projection ) throws IOException { super( ); this.projection = projection; read( in, projection ); } private static class Row { public float centerLat; public float centerLon; public float depth; public Row( float centerLat, float centerLon, float depth ) { this.centerLat = centerLat; this.centerLon = centerLon; this.depth = depth; } } protected void read( InputStream in, GeoProjection tp ) throws IOException { BufferedReader reader = new BufferedReader( new InputStreamReader( in ) ); // create a sorted set to store all the rows from the data input file of the form: // latitude longitude depth NavigableSet<Row> rows = new TreeSet<Row>( new Comparator<Row>( ) { @Override public int compare( Row o1, Row o2 ) { int latComparison = Double.compare( o1.centerLat, o2.centerLat ); return ( latComparison != 0 ? latComparison : Double.compare( o1.centerLon, o2.centerLon ) ); } } ); // read lines from the input stream into the set String line = null; while ( ( line = reader.readLine( ) ) != null ) { if ( line.trim( ).isEmpty( ) ) continue; String[] tokens = line.trim( ).split( "[ ]+" ); float lon = Float.parseFloat( tokens[0] ); float lat = Float.parseFloat( tokens[1] ); float depth = Float.parseFloat( tokens[2] ); rows.add( new Row( lat, lon, depth ) ); } // create a set with only unique latitudes (in sorted order) NavigableSet<Row> uniqueLatitudes = new TreeSet<Row>( new Comparator<Row>( ) { @Override public int compare( Row o1, Row o2 ) { return Double.compare( o1.centerLat, o2.centerLat ); } } ); // create a set with only unique longitudes (in sorted order) NavigableSet<Row> uniqueLongitudes = new TreeSet<Row>( new Comparator<Row>( ) { @Override public int compare( Row o1, Row o2 ) { return Double.compare( o1.centerLon, o2.centerLon ); } } ); uniqueLatitudes.addAll( rows ); uniqueLongitudes.addAll( rows ); // retrieve the number of unique latitudes and longitudes from the sets imageHeight = uniqueLatitudes.size( ); imageWidth = uniqueLongitudes.size( ); // calculate the average step size moving along latitude and longitude widthStep = 0; Row prevRow = null; for ( Row row : uniqueLongitudes ) { if ( prevRow != null ) { widthStep += row.centerLon - prevRow.centerLon; } prevRow = row; } widthStep = widthStep / ( uniqueLongitudes.size( ) - 1 ); heightStep = 0; prevRow = null; for ( Row row : uniqueLatitudes ) { if ( prevRow != null ) { heightStep += row.centerLat - prevRow.centerLat; } prevRow = row; } heightStep = heightStep / ( uniqueLatitudes.size( ) - 1 ); // find the lat and lon of the starting corner startLon = uniqueLongitudes.first( ).centerLon - 0.5 * widthStep; startLat = uniqueLatitudes.first( ).centerLat - 0.5 * heightStep; data = new double[imageWidth][imageHeight]; for ( Row row : rows ) { int x = ( int ) Math.floor( ( row.centerLon - startLon ) / widthStep ); int y = ( int ) Math.floor( ( row.centerLat - startLat ) / heightStep ); if ( x < 0 ) x = 0; if ( x >= imageWidth ) x = imageWidth - 1; if ( y < 0 ) y = 0; if ( y >= imageHeight ) y = imageHeight - 1; data[x][y] = row.depth; } startLon = Angle.normalizeAngle180( startLon ); } public FloatTextureProjected2D getTexture( ) { // create an OpenGL texture wrapper object FloatTextureProjected2D texture = new FloatTextureProjected2D( imageWidth, imageHeight ); Projection projection = getProjection( ); texture.setProjection( projection ); texture.setData( data ); return texture; } public LatLonProjection getProjection( ) { double endLat = startLat + heightStep * imageHeight; double endLon = startLon + widthStep * imageWidth; return new LatLonProjection( projection, startLat, endLat, startLon, endLon, false ); } public void setAxisBounds( Axis2D axis ) { axis.getAxisX( ).setMin( getMinX( ) ); axis.getAxisX( ).setMax( getMaxX( ) ); axis.getAxisY( ).setMin( getMinY( ) ); axis.getAxisY( ).setMax( getMaxY( ) ); } public double getStartLon( ) { return startLon; } public double getStartLat( ) { return startLat; } public double getWidthStep( ) { return widthStep; } public double getHeightStep( ) { return heightStep; } public int getImageHeight( ) { return imageHeight; } public int getImageWidth( ) { return imageWidth; } public double getMinX( ) { Vector2d swCorner = projection.project( LatLonGeo.fromDeg( startLat, startLon ) ); Vector2d neCorner = projection.project( LatLonGeo.fromDeg( startLat + heightStep * imageHeight, startLon + widthStep * imageWidth ) ); return Math.min( swCorner.getX( ), neCorner.getX( ) ); } public double getMaxX( ) { Vector2d swCorner = projection.project( LatLonGeo.fromDeg( startLat, startLon ) ); Vector2d neCorner = projection.project( LatLonGeo.fromDeg( startLat + heightStep * imageHeight, startLon + widthStep * imageWidth ) ); return Math.max( swCorner.getX( ), neCorner.getX( ) ); } public double getMinY( ) { Vector2d swCorner = projection.project( LatLonGeo.fromDeg( startLat, startLon ) ); Vector2d neCorner = projection.project( LatLonGeo.fromDeg( startLat + heightStep * imageHeight, startLon + widthStep * imageWidth ) ); return Math.min( swCorner.getY( ), neCorner.getY( ) ); } public double getMaxY( ) { Vector2d swCorner = projection.project( LatLonGeo.fromDeg( startLat, startLon ) ); Vector2d neCorner = projection.project( LatLonGeo.fromDeg( startLat + heightStep * imageHeight, startLon + widthStep * imageWidth ) ); return Math.max( swCorner.getY( ), neCorner.getY( ) ); } public double[][] getData( ) { return data; } }