/** @file CalibCoverageDialog.java * * @author marco corvi * @date jan 2012 * * @brief TopoDroid calibration data distribution display * -------------------------------------------------------- * Copyright This sowftare is distributed under GPL-3.0 or later * See the file COPYING. * -------------------------------------------------------- */ package com.topodroid.DistoX; import java.util.List; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.app.Dialog; import android.os.Bundle; import android.widget.Toast; import android.content.Context; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.view.View; public class CalibCoverageDialog extends MyDialog implements View.OnClickListener { private class Direction { public float mCompass; public float mClino; public float mValue; public Direction( float cm, float cl, float v ) { mCompass = cm; mClino = cl; mValue = v; } } private static final int DIMY = 19; private static final int WIDTH = 180; private static final int HEIGHT = 90; private static final int AZIMUTH_BIT = 16; private int[] clino_angles; private int[] t_size; private int[] t_offset; private int t_dim; private Direction angles[]; private float mCoverage; private Bitmap mBitmap; private List<CalibCBlock> mList; private CalibAlgo mCalib; private TextView mText; private ImageView mImage; private Button mBtnEval; private Button mBtnEvalCal; // private Button mBtnBack; public CalibCoverageDialog( Context context, List< CalibCBlock > list, CalibAlgo cal ) { super( context, R.string.CalibCoverageDialog ); mCalib = cal; mList = list; clino_angles = new int[ DIMY ]; t_size = new int[ DIMY ]; t_offset = new int[ DIMY ]; mBitmap = Bitmap.createBitmap( WIDTH, HEIGHT, Bitmap.Config.ARGB_8888 ); setup(); evalCoverage( mList, null ); fillImage(); } @Override public void onCreate( Bundle bundle ) { super.onCreate( bundle ); initLayout( R.layout.calib_coverage_dialog, R.string.title_coverage ); mText = (TextView) findViewById( R.id.coverage_value ); mImage = (ImageView) findViewById( R.id.coverage_image ); mBtnEval = (Button) findViewById( R.id.coverage_eval ); mBtnEvalCal = (Button) findViewById( R.id.coverage_eval_cal ); // mBtnBack = (Button) findViewById( R.id.coverage_back ); mBtnEval.setOnClickListener( this ); mBtnEvalCal.setOnClickListener( this ); // mBtnBack.setOnClickListener( this ); reset(); } private void reset() { mImage.setImageBitmap( mBitmap ); mText.setText( Float.toString( mCoverage ) ); } public void onClick(View v) { Button btn = (Button)v; if ( btn == mBtnEval ) { evalCoverage( mList, null ); fillImage(); reset(); } else if ( btn == mBtnEvalCal ) { if ( mCalib.GetAG() != null ) { evalCoverage( mList, mCalib ); fillImage(); reset(); } else { Toast.makeText( mContext, R.string.no_calib, Toast.LENGTH_SHORT ).show(); } } else { dismiss(); } } private void setup() { int i; for ( i=0; i<19; ++i ) { // clino angles: from +90 to -90 clino_angles[i] = 90 - 10*i; } t_size[ 0 ] = t_size[18] = 1; for ( i=1; i<9; ++i ) { t_size[i] = t_size[18-i] = AZIMUTH_BIT * i; } t_size[ 9 ] = AZIMUTH_BIT * 9; // max azimuth steps 54 at clino 0 t_offset[0] = 0; for ( i=1; i<19; ++i ) { t_offset[i] = t_offset[i-1] + t_size[i-1]; } t_dim = t_offset[18] + t_size[18]; angles = new Direction [ t_dim ]; for (int k = 0; k<19; ++k ){ float clino = clino_angles[k] * TDMath.GRAD2RAD; for (int j=t_offset[k]; j<t_offset[k]+t_size[k]; ++j ) { angles[j] = new Direction( TDMath.M_PI + ( TDMath.M_2PI * (j - t_offset[k]) ) / t_size[k], clino, 1.0f ); } } } private float cosine( float compass1, float clino1, float compass2, float clino2 ) { double h1 = Math.cos( clino1 ); double z1 = Math.sin( clino1 ); double x1 = h1 * Math.cos( compass1 ); double y1 = h1 * Math.sin( compass1 ); double h2 = Math.cos( clino2 ); double z2 = Math.sin( clino2 ); double x2 = h2 * Math.cos( compass2 ); double y2 = h2 * Math.sin( compass2 ); return (float)(x1*x2 + y1*y2 + z1*z2); // cosine of the angle } private void updateDirections( float compass, float clino, int cnt ) { for (int j=0; j<t_dim; ++j ) { float c = cosine( compass, clino, angles[j].mCompass, angles[j].mClino ); if ( c > 0.0 ) { c = c * c; angles[j].mValue -= (cnt >= 4)? c*c : c*c * cnt * 0.25f; if ( angles[j].mValue < 0.0f ) angles[j].mValue = 0.0f; } } } private void evalCoverage( List<CalibCBlock> clist, CalibAlgo transform ) { for (int j=0; j<t_dim; ++j ) angles[j].mValue = 1.0f; long old_grp = 0; float compass_avg = 0.0f; float clino_avg = 0.0f; int cnt_avg = 0; for ( CalibCBlock b : clist ) { if ( b.mGroup == 0 ) continue; if ( transform == null ) { b.computeBearingAndClino( ); } else { b.computeBearingAndClino( transform ); } float compass = b.mBearing * TDMath.GRAD2RAD; float clino = b.mClino * TDMath.GRAD2RAD; if ( b.mGroup == old_grp ) { if ( cnt_avg > 0 && Math.abs( compass - compass_avg / cnt_avg ) > 1.5f * TDMath.M_PI ) { if ( compass > TDMath.M_PI ) { compass -= TDMath.M_2PI; // average around 0 } else { compass += TDMath.M_2PI; // average around 360 } } clino_avg += clino; compass_avg += compass; cnt_avg ++; } else { if ( cnt_avg > 0 ) { compass_avg /= cnt_avg; clino_avg /= cnt_avg; updateDirections( compass_avg, clino_avg, cnt_avg ); } clino_avg = clino; compass_avg = compass; cnt_avg = 1; old_grp = b.mGroup; } } if ( cnt_avg > 0 ) { compass_avg /= cnt_avg; clino_avg /= cnt_avg; updateDirections( compass_avg, clino_avg, cnt_avg ); } mCoverage = 0.0f; for (int j=0; j<t_dim; ++j ) { mCoverage += angles[j].mValue; } mCoverage = 100.0f * ( 1.0f - mCoverage/t_dim ); } private void fillImage( ) // image is 90 * 180 * 4 { for (int j0=0; j0<HEIGHT; ++j0) { int j = 2 * j0; float clino = j - 90.0f; int j1 = j/10; int j2 = j1 + 1; float d = (j%10)/10.0f; int j1off = t_offset[j1]; int j2off = t_offset[j2]; float amax = 180.0f * TDMath.sqrt( 1 - (clino/90.0f)*(clino/90.0f) ); // if ( amax < 1.0 ) amax = 1.0; int ioff = (180 - (int)(amax)) / 2; if (ioff < 0 ) ioff = 0; int ixold = -1; for (int i0=0; i0<WIDTH; ++i0) { int i = 2 * i0; float compass = ((i + 180)%360); // N middle, W left, E right int ix = (int)(compass / 180.0f * amax); // from 0 to 2*amax ix /= 2; if ( ix == ixold ) continue; ixold = ix; float c1 = compass/360.0f * t_size[j1]; float c2 = compass/360.0f * t_size[j2]; int i11 = (int)(c1); // index in [0, t_size) int i21 = (int)(c2); int i12 = (i11 + 1)%t_size[j1]; float d1 = c1 - i11; int i22 = (i21 + 1)%t_size[j2]; float d2 = c2 - i21; float v1 = angles[j1off+i11].mValue * (1-d1) + angles[j1off+i12].mValue * d1; float v2 = angles[j2off+i21].mValue * (1-d2) + angles[j2off+i22].mValue * d2; float v = v1 * (1-d) + v2 * d; // int off = (j0*WIDTH + (ioff + ix))*BYTES; int green = ( v > 254 )? 254 : (int)(254*v); int red = 0xff - green; int col = 0xff000000 | (red << 8 ) | (green << 16); mBitmap.setPixel( ioff+ix, j0, col ); } } } }