/*
* 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;
}
}