Question

I have a bitmap of temperatures across the US that gets displayed as a map overlay. That part works fine- the Bitmap temperature overlay sits perfectly on the map relative to borders and geographic points.

My goal is to be able to tap this temperature bitmap and then output the temperature based on that pixel color. The Bitmap overlay is getting scaled and positioned based on the current map projection. My solution to getting the bitmap pixel when tapped requires me to go backwards with a geopoint, account for scaling, and then retrieve the tapped point from the original bitmap image.

The tapping works fine but I have noticed inaccuracies as far as which pixel is retrieved. For example I can tap below the visible part of the bitmap (like in mexico) and still return a colored pixel- Conversely at the top of the map (near Canada) I can tap on the visible bitmap and not return any pixels. It is as if the returned pixel values are all shifted south by about 50 miles. The errors aren't huge or obvious when zoomed out but when zoomed in they become obvious as obviously wrong pixel (temperature) values are returned.

I think my method is sound but I am wondering if the large numbers I am dealing with are causing problems with "doubles". Do I need to switch to BigDecimal (no experience there) or some other number format? Is there a better way to do this?

CODE BELOW>>>>

public class BitmapOverlay extends Overlay {

Bitmap bmp;
Bitmap bmp2;
Context mContext;
GeoPoint upperLeft;
GeoPoint lowerRight;
Paint paint;
Paint lpaint;
Point ppupperLeft;
Point pplowerRight;
Rect src;
Rect src2;
Rect dst;
int frame;
int complete=100;

boolean shadow;
AnimationDrawable animation;
ImageView radarView;

Double W=(-129.357400913)* 1E6;
Double S=(23.560962423770285)* 1E6;
Double E=(-64.6787004566)* 1E6;
Double N=(50.3092170302897)* 1E6;

Projection currentProjection;

boolean mExternalStorageAvailable = false;
String fullPath;

boolean running;

public BitmapOverlay (Context freshContext){

    paint = new Paint(Paint.FILTER_BITMAP_FLAG);

    paint.setAntiAlias(true);
    paint.setFilterBitmap(true);
    paint.setDither(true);



    String state = Environment.getExternalStorageState();

    if (Environment.MEDIA_MOUNTED.equals(state)) {
    // We can read and write the media
    mExternalStorageAvailable = true;

    Log.i("isSdReadable", "External storage card is readable.");

    } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
    // We can only read the media
    Log.i("isSdReadable", "External storage card is readable.");
    mExternalStorageAvailable = true;
    } else {
    // Something else is wrong. It may be one of many other
    // states, but all we need to know is we can neither read nor write
    mExternalStorageAvailable = false;
    }








    upperLeft=new GeoPoint(N.intValue(),W.intValue());
    lowerRight=new GeoPoint(S.intValue(),E.intValue());


    ppupperLeft = new Point();
    pplowerRight = new Point();



    mContext=freshContext;




}






public void UpdateFrame(int sentframe , String ET){
    frame=sentframe;
//  complete=sentcomplete;




    if ( mExternalStorageAvailable == true) {

    //  Log.i("MyRadarOverlay", "got BMP Frame="+frame);

        //have to convert complete to frame to sequence... otherwise it gets to 5     
and stops..
        //might need to figure better wayt o tell when bmp is done
        //complete=frame-1;

        if (ET.equals("temp")){fullPath =  
Environment.getExternalStorageDirectory().getAbsolutePath() +  
                "/Pictures/ti0.png";}

        if (ET.equals("radar")){fullPath = 
Environment.getExternalStorageDirectory().getAbsolutePath() +  
                "/Pictures/ri"+frame+".png";}



        // Look for the file on the external storage
        try {



        bmp = BitmapFactory.decodeFile(fullPath);
        //}
        } catch (Exception e) {
        Log.e("getThumbnail() on external storage", e.getMessage());
        }


        if (ET.equals("radar")){    
        fullPath = Environment.getExternalStorageDirectory().getAbsolutePath() +  
                "/Pictures/si"+frame+".png";

    //  Log.i("MyRadarOverlay", "sifile path="+fullPath);

        // Look for the file on the external storage
        try {
        //if (tools.isSdReadable() == true) {

        bmp2 = BitmapFactory.decodeFile(fullPath);
        //}
        } catch (Exception e) {
        Log.e("getThumbnail() on external storage", e.getMessage());
        }
        //end of second if et == radar
 }

    }



}




  public void draw(Canvas canvas, MapView mapView, boolean shadow) {



    currentProjection=mapView.getProjection();

    currentProjection.toPixels(upperLeft, ppupperLeft);
    currentProjection.toPixels(lowerRight, pplowerRight);



    dst = new Rect( ppupperLeft.x, ppupperLeft.y, pplowerRight.x, pplowerRight.y );

    if (bmp2 != null ){

        src = new Rect( 0,0, bmp2.getWidth() , bmp2.getHeight()  );

        canvas.drawBitmap(bmp2, src, dst, paint);


      } 



  if (bmp != null ){


    src = new Rect( 0,0, bmp.getWidth() , bmp.getHeight()  );

    canvas.drawBitmap(bmp, src, dst, paint);



  } 



}




//###################ON TAP#################################   


@Override
public boolean onTap( GeoPoint geoPoint, MapView mapView) {



//  Log.i("My Info", "overlay-on tap called");
     Log.i("BITMAP OVERALY", "TAP at"+geoPoint);     

 //  take left lon add right lon...add basically get a range of lons that the point can  
fall under//
//   if it falls within the range get the percentage ...


 //  the start left most lon is equivalent to 0 the right most is equavalent to the 
width--1600
//   find the relative position on that range

     double xpixel=0;
     double ypixel=0;

     double latgeopoint=geoPoint.getLatitudeE6();
     double longeopoint=geoPoint.getLongitudeE6();


    //find out if it is within longitude range of bitmap
    if (longeopoint>W && longeopoint<E){
        //find out if it is within latitude range of bitmap
        if (latgeopoint>S && latgeopoint<N){

        Log.i("BITMAP OVERALY", "passed if statement- 
lon,lat"+longeopoint+","+latgeopoint);

        //work  on lon side
        double lonrange=W-longeopoint;
        Log.i("BITMAP OVERALY", "lonrange="+lonrange);

        //-64678700456
        double lonratio=lonrange/W-E;
        Log.i("BITMAP OVERALY", "lonratio="+lonratio);

        xpixel=lonratio*bmp.getWidth();
        Log.i("BITMAP OVERALY", "xpixel="+xpixel);

        //work on lat side

        double latrange=N-latgeopoint;
        Log.i("BITMAP OVERALY", "latrange="+latrange);

        //26748254607
        double latratio=latrange/N-S;
        Log.i("BITMAP OVERALY", "latratio="+latratio);

        ypixel=latratio*bmp.getHeight();
        Log.i("BITMAP OVERALY", "ypixel="+ypixel);


        //end if lat
        }
    //end if lon    
    }


     //convert doubles to ints
    int pixelx = (int)xpixel;
    int pixely = (int)ypixel;

    Log.i("BITMAP OVERALY", "pixelx,pixely="+pixelx+","+pixely);



    int pixel = bmp.getPixel(pixelx, pixely);



    int redValue = Color.red(pixel);
    int greenValue = Color.green(pixel);
    int blueValue = Color.blue(pixel);

    float[] hsv = new float[3];
    Color.RGBToHSV(redValue, greenValue, blueValue, hsv);

    String mytemp="";



    if (hsv[0]<5){mytemp="Greater than 110";}
    if (hsv[0]<=20 && hsv[0]>5){mytemp="100 to 110";}
    if (hsv[0]<=45 && hsv[0]>20){mytemp="90 to 100";}
    if (hsv[0]<=60 && hsv[0]>45){mytemp="80 to 90";}
    if (hsv[0]<=80 && hsv[0]>60){mytemp="70 to 80";}
    if (hsv[0]<=120 && hsv[0]>80){mytemp="60 to 70";}
    if (hsv[0]<=150 && hsv[0]>120){mytemp="50 to 60";}







            AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);


              dialog.setNegativeButton("Close",
                      new DialogInterface.OnClickListener() {
                          public void onClick(DialogInterface dialog, int id) {

                                dialog.cancel();

                          }
                      });


              dialog.setTitle("BITMAP TAP hsv=" + hsv[0] + " " + hsv[1] + " " + 
hsv[2] + " TEMP="+mytemp);



              dialog.show();



    return false; 
 //end on tap   
} 



}
Was it helpful?

Solution

In case anyone else stumbles upon this- I found a solution. Apparently the error is due to the web mercator map translation that takes place. The bitmap I am overlaying upon the map is only accurate at the far north and south bounds. That is where the error is zero. The closer I go to the midpoint of my bitmap relative to latitude the error grows. I was able to quantify the error at the mid point by drawing a set of pixels on the bitmap and then comparing them to known lat lines. In this particular case the error was about 54 pixels at the midpoint. I also tested a few other points north and south at known latitudes in order to create a dataset of error. Now that I know my error I can interpolate a correction to apply at each particular latitude. In order to accurately interpolate I am using Cubic Interpolation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top