/*
 
* 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.dnc;

import static com.metsci.glimpse.dnc.DncTangentPlanes.azimuthToTangentPlane_MATHRAD;
import static com.metsci.glimpse.dnc.DncTangentPlanes.posToTangentPlane;
import static com.metsci.glimpse.util.units.Angle.degreesToRadians;
import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;

import com.metsci.glimpse.dnc.DncTangentPlanes.DncTangentPlane;
import com.metsci.glimpse.util.vector.Vector2d;

public class DncProjections
{


    
public interface DncProjection
    
{
        
String configString( );

        
double suggestedPpvMultiplier( );

        
boolean canProjectLibrary( int databaseNum, String libraryName, double minLat_DEG, double maxLat_DEG, double minLon_DEG, double maxLon_DEG );

        
void projectPos( double lat_DEG, double lon_DEG, float[] result, int resultOffset );

        
double projectAzimuth_MATHRAD( double x, double y, double azimuth_MATHRAD );
    
}



    
public static boolean canProjectBrowse( DncProjection proj )
    
{
        
return proj.canProjectLibrary( 1, "BROWSE", -90, 90, -180, 180 );
    
}



    
public static final DncProjection dncPlateCarree = new DncProjection( )
    
{
        
public String configString( )
        
{
            
return "PlateCarree";
        
}

        
public double suggestedPpvMultiplier( )
        
{
            
return 1;
        
}

        
public boolean canProjectLibrary( int databaseNum, String libraryName, double minLat_DEG, double maxLat_DEG, double minLon_DEG, double maxLon_DEG )
        
{
            
return true;
        
}

        
public void projectPos( double lat_DEG, double lon_DEG, float[] result, int resultOffset )
        
{
            
result[ resultOffset + 0 ] = ( float ) lon_DEG;
            
result[ resultOffset + 1 ] = ( float ) lat_DEG;
        
}

        
public double projectAzimuth_MATHRAD( double x, double y, double azimuth_MATHRAD )
        
{
            
double cos_LOCAL = cos( azimuth_MATHRAD );
            
double sin_LOCAL = sin( azimuth_MATHRAD );

            
double lat_DEG = y;
            
double cosLat = cos( degreesToRadians( lat_DEG ) );
            
double cos_PROJ = cos_LOCAL / cosLat;
            
double sin_PROJ = sin_LOCAL;

            
return atan2( sin_PROJ, cos_PROJ );
        
}
    
};



    
public static final DncProjection dncTangentPlane( double tangentLat_DEG, double tangentLon_DEG )
    
{
        
return dncTangentPlane( tangentLat_DEG, tangentLon_DEG, 0.0, 0.0 );
    
}

    
public static final DncProjection dncTangentPlane( double tangentLat_DEG, double tangentLon_DEG, Vector2d tangentPointOnPlane )
    
{
        
return dncTangentPlane( tangentLat_DEG, tangentLon_DEG, tangentPointOnPlane.getX( ), tangentPointOnPlane.getY( ) );
    
}

    
public static final DncProjection dncTangentPlane( final double tangentLat_DEG, final double tangentLon_DEG, final double tangentPointOnPlaneX, final double tangentPointOnPlaneY )
    
{
        
return new DncProjection( )
        
{
            
DncTangentPlane plane = new DncTangentPlane( tangentLat_DEG, tangentLon_DEG );

            
public String configString( )
            
{
                
return "TangentPlane[ " + plane.tangentLat_DEG + "," + plane.tangentLon_DEG + " ]";
            
}

            
public double suggestedPpvMultiplier( )
            
{
                
// Larger -> more crowded display
                
// Smaller -> emptier display
                
double empiricalTweakFactor = 2;

                
float[] p0 = new float[ 2 ];
                
projectPos( plane.tangentLat_DEG, plane.tangentLon_DEG - 0.5, p0, 0 );

                
float[] p1 = new float[ 2 ];
                
projectPos( plane.tangentLat_DEG, plane.tangentLon_DEG + 0.5, p1, 0 );

                
float dx = p0[ 0 ] - p1[ 0 ];
                
float dy = p0[ 1 ] - p1[ 1 ];
                
return sqrt( empiricalTweakFactor * ( dx*dx + dy*dy ) );
            
}

            
public boolean canProjectLibrary( int databaseNum, String libraryName, double minLat_DEG, double maxLat_DEG, double minLon_DEG, double maxLon_DEG )
            
{
                
double antipodeLat_DEG = -tangentLat_DEG;
                
double antipodeLon_DEG = normalizeLon( minLon_DEG, tangentLon_DEG + 180 );
                
boolean libraryContainsAntipode = ( minLat_DEG <= antipodeLat_DEG && antipodeLat_DEG <= maxLat_DEG && minLon_DEG <= antipodeLon_DEG && antipodeLon_DEG <= maxLon_DEG );
                
return ( !libraryContainsAntipode );
            
}

            
protected double normalizeLon( double minLon_DEG, double lon_DEG )
            
{
                
double offset_DEG = ( lon_DEG - minLon_DEG ) % 360;
                
if ( offset_DEG < 0 ) offset_DEG += 360;
                
return ( minLon_DEG + offset_DEG );
            
}

            
public void projectPos( double lat_DEG, double lon_DEG, float[] result, int resultOffset )
            
{
                
posToTangentPlane( plane, lat_DEG, lon_DEG, result, resultOffset );
            
}

            
public double projectAzimuth_MATHRAD( double x, double y, double azimuth_MATHRAD )
            
{
                
return azimuthToTangentPlane_MATHRAD( plane, x, y, azimuth_MATHRAD );
            
}
        
};
    
}


}