2011-11-02 22 views
32

Tôi sắp xếp về cách thực hiện "la bàn cá nhân", nghĩa là la bàn trỏ đến vòng bi cụ thể thay vì tiêu chuẩn "cực bắc" ... thật không may, nỗ lực hiện tại của tôi đã xảy ra sai (không chỉ vào vòng bi đã cho). Nó cũng được kết nối với máy gia tốc để có thể tự điều chỉnh chính nó dựa trên cách người dùng đang chuyển.Xoay một ImageView giống như la bàn (với "cực bắc" được đặt ở nơi khác)

Dưới đây là nỗ lực hiện tại của tôi vào nó (các onSensorChanged() -method mà cập nhật vào mũi tên):

public void onSensorChanged(SensorEvent event) { 

      // If we don't have a Location, we break out 
      if (LocationObj == null) return; 

      float azimuth = event.values[0]; 
          float baseAzimuth = azimuth; 

      GeomagneticField geoField = new GeomagneticField(Double 
        .valueOf(LocationObj.getLatitude()).floatValue(), Double 
        .valueOf(LocationObj.getLongitude()).floatValue(), 
        Double.valueOf(LocationObj.getAltitude()).floatValue(), 
        System.currentTimeMillis()); 
      azimuth += geoField.getDeclination(); // converts magnetic north into true north 

      //Correct the azimuth 
      azimuth = azimuth % 360; 

      //This is where we choose to point it 
      float direction = azimuth + LocationObj.bearingTo(destinationObj); 
      rotateImageView(arrow, R.drawable.arrow, direction); 

      //Set the field 
      if(baseAzimuth > 0 && baseAzimuth < 45) fieldBearing.setText("S"); 
      else if(baseAzimuth >= 45 && baseAzimuth < 90) fieldBearing.setText("SW"); 
      else if(baseAzimuth > 0 && baseAzimuth < 135) fieldBearing.setText("W"); 
      else if(baseAzimuth > 0 && baseAzimuth < 180) fieldBearing.setText("NW"); 
      else if(baseAzimuth > 0 && baseAzimuth < 225) fieldBearing.setText("N"); 
      else if(baseAzimuth > 0 && baseAzimuth < 270) fieldBearing.setText("NE"); 
      else if(baseAzimuth > 0 && baseAzimuth < 315) fieldBearing.setText("E"); 
      else if(baseAzimuth > 0 && baseAzimuth < 360) fieldBearing.setText("SE"); 
      else fieldBearing.setText("?"); 

     } 

Và đây là phương pháp mà xoay ImageView (rotateImageView()):

private void rotateImageView(ImageView imageView, int drawable, float rotate) { 

    // Decode the drawable into a bitmap 
    Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(), 
      drawable); 

    // Get the width/height of the drawable 
    DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); 
    int width = bitmapOrg.getWidth(), height = bitmapOrg.getHeight(); 

    // Initialize a new Matrix 
    Matrix matrix = new Matrix(); 

    // Decide on how much to rotate 
    rotate = rotate % 360; 

    // Actually rotate the image 
    matrix.postRotate(rotate, width, height); 

    // recreate the new Bitmap via a couple conditions 
    Bitmap rotatedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, width, height, matrix, true); 
    //BitmapDrawable bmd = new BitmapDrawable(rotatedBitmap); 

    //imageView.setImageBitmap(rotatedBitmap); 
    imageView.setImageDrawable(new BitmapDrawable(getResources(), rotatedBitmap)); 
    imageView.setScaleType(ScaleType.CENTER); 
} 

Bất kỳ trợ giúp sẽ được nhiều đánh giá cao, vì tôi không hoàn toàn biết cách tiến hành. Các "bài đọc" tôi nhận được trong khi thử nó ra là hơi không chính xác và điểm sai hướng. Tôi có làm điều gì đó thực sự không, hay tôi chỉ có một thử nghiệm thực sự tồi tệ?

+0

Tôi có thể hỏi liệu phương pháp này cập nhật hình ảnh quá CPU-costy? Khi tôi làm theo mã, màn hình cực kỳ chậm và tôi thậm chí không thể phóng to hoặc thu nhỏ. –

+1

@ perfectionm1ng Tôi đã không nhận thấy bất cứ điều gì như thế, và đó là gần hai năm trở lại. Có thể là sự kết hợp của sự vật? :-) – ninetwozero

Trả lời

51

Chức năng rotateImageView của bạn sẽ hoạt động tốt, tuy nhiên có một số thứ cần phải thay đổi trong tính toán xoay vòng của bạn.

//This is where we choose to point it 
float direction = azimuth + LocationObj.bearingTo(destinationObj); 
rotateImageView(arrow, R.drawable.arrow, direction); 

Vấn đề là bearingTo sẽ cho bạn phạm vi từ -180 đến 180, điều này sẽ gây nhầm lẫn cho mọi thứ một chút. Chúng tôi sẽ cần phải chuyển đổi giá trị này thành một phạm vi từ 0 đến 360 để có được vòng quay chính xác.

Đây là một bảng của những gì chúng ta thực sự muốn, so sánh với những gì bearingTo cho chúng ta

 
+-----------+--------------+ 
| bearingTo | Real bearing | 
+-----------+--------------+ 
| 0   | 0   | 
+-----------+--------------+ 
| 90  | 90   | 
+-----------+--------------+ 
| 180  | 180   | 
+-----------+--------------+ 
| -90  | 270   | 
+-----------+--------------+ 
| -135  | 225   | 
+-----------+--------------+ 
| -180  | 180   | 
+-----------+--------------+ 

Mặc dù bearingTo là trong khoảng -180 đến 180, 0 vẫn là phía bắc đúng mà sẽ để lại cho chúng tôi tính toán này:

// Store the bearingTo in the bearTo variable 
float bearTo = LocationObj.bearingTo(destinationObj); 

// If the bearTo is smaller than 0, add 360 to get the rotation clockwise. 
if (bearTo < 0) { 
    bearTo = bearTo + 360; 
} 

Nếu chúng ta thêm một số giá trị giả để thử nghiệm công thức mới của chúng tôi:

float bearTo = -100; 
// This will now equal to true 
if (-100 < 0) { 
    bearTo = -100 + 360 = 360 - 100 = 260; 
} 

Bây giờ chúng tôi đã sắp xếp ra bearingTo, cho phép đầu vào góc phương vị!

Bạn cần phải trừ đi độ lệch thay vì thêm nó, vì chúng tôi muốn góc phương vị là 0 khi chúng tôi trỏ trực tiếp điện thoại vào đúng hướng bắc thay vì có độ lệch thêm vào góc phương vị. chúng tôi chỉ điện thoại đến phía bắc thực. Sửa lỗi này bằng cách trừ đi sự sụt giảm thay vì thêm nó.

azimuth -= geoField.getDeclination(); // converts magnetic north into true north 

Khi chúng ta xoay điện thoại để bắc đúng bây giờ, góc phương vị sau đó sẽ bằng 0

Mã của bạn để sửa góc phương vị là không còn cần thiết.

// Remove/uncomment this line 
azimuth = azimuth % 360; 

Bây giờ chúng tôi sẽ tiếp tục đến điểm chúng tôi tính toán vòng quay thực. Nhưng trước tiên tôi sẽ tóm tắt những loại giá trị hiện có và giải thích chúng thực sự là gì:

bearTo = Góc từ phía bắc đến vị trí đích từ điểm chúng tôi hiện đang đứng.

góc phương vị = Góc bạn đã xoay điện thoại của mình từ phía bắc thực.

Bằng cách nói điều này, nếu bạn trỏ điện thoại của bạn trực tiếp ở phía bắc thực, chúng tôi thực sự muốn mũi tên xoay góc mà bearTo được đặt thành. Nếu bạn trỏ điện thoại của bạn 45 độ từ phía bắc thực, chúng tôi muốn mũi tên quay 45 độ ít hơn so với bearTo.Điều này để lại cho chúng tôi các tính toán sau:

float direction = bearTo - azimuth; 

Tuy nhiên, nếu chúng tôi đưa vào một số giá trị giả: bearTo = 45; góc phương vị = 180;

direction = 45 - 180 = -135; 

Điều này có nghĩa là mũi tên phải xoay 135 độ ngược chiều kim đồng hồ. Chúng tôi sẽ cần phải đặt trong một điều kiện tương tự như chúng tôi đã làm với bearTo!

// If the direction is smaller than 0, add 360 to get the rotation clockwise. 
if (direction < 0) { 
    direction = direction + 360; 
} 

Chữ viết, chữ N, E, S và W bị tắt, vì vậy tôi đã sửa chúng theo phương thức cuối cùng bên dưới.

phương pháp onSensorChanged của bạn sẽ trông như thế này:

public void onSensorChanged(SensorEvent event) { 

    // If we don't have a Location, we break out 
    if (LocationObj == null) return; 

    float azimuth = event.values[0]; 
    float baseAzimuth = azimuth; 

    GeomagneticField geoField = new GeomagneticField(Double 
     .valueOf(LocationObj.getLatitude()).floatValue(), Double 
     .valueOf(LocationObj.getLongitude()).floatValue(), 
     Double.valueOf(LocationObj.getAltitude()).floatValue(), 
     System.currentTimeMillis()); 

    azimuth -= geoField.getDeclination(); // converts magnetic north into true north 

    // Store the bearingTo in the bearTo variable 
    float bearTo = LocationObj.bearingTo(destinationObj); 

    // If the bearTo is smaller than 0, add 360 to get the rotation clockwise. 
    if (bearTo < 0) { 
     bearTo = bearTo + 360; 
    } 

    //This is where we choose to point it 
    float direction = bearTo - azimuth; 

    // If the direction is smaller than 0, add 360 to get the rotation clockwise. 
    if (direction < 0) { 
     direction = direction + 360; 
    } 

    rotateImageView(arrow, R.drawable.arrow, direction); 

    //Set the field 
    String bearingText = "N"; 

    if ((360 >= baseAzimuth && baseAzimuth >= 337.5) || (0 <= baseAzimuth && baseAzimuth <= 22.5)) bearingText = "N"; 
    else if (baseAzimuth > 22.5 && baseAzimuth < 67.5) bearingText = "NE"; 
    else if (baseAzimuth >= 67.5 && baseAzimuth <= 112.5) bearingText = "E"; 
    else if (baseAzimuth > 112.5 && baseAzimuth < 157.5) bearingText = "SE"; 
    else if (baseAzimuth >= 157.5 && baseAzimuth <= 202.5) bearingText = "S"; 
    else if (baseAzimuth > 202.5 && baseAzimuth < 247.5) bearingText = "SW"; 
    else if (baseAzimuth >= 247.5 && baseAzimuth <= 292.5) bearingText = "W"; 
    else if (baseAzimuth > 292.5 && baseAzimuth < 337.5) bearingText = "NW"; 
    else bearingText = "?"; 

    fieldBearing.setText(bearingText); 

} 
+0

Cảm ơn Chris, được đánh dấu là câu trả lời. :-) – ninetwozero

+0

Câu trả lời hay! Một tiết kiệm thời gian như vậy. –

+0

Sau khi thử nghiệm một số, tôi thấy điều này có thể tắt khi điện thoại quay sang chế độ nằm ngang. –

3

Bạn sẽ có thể đặt ma trận thành ImageView mà không cần phải tạo lại bitmap mỗi lần, và er .. 'normalize' (là từ đó?) Các bài đọc.

float b = mLoc.getBearing(); 
if(b < 0) 
    b = 360 + b; 
float h = item.mHeading; 
if(h < 0) 
    h = 360 + h; 
float r = (h - b) - 360; 
matrix.reset(); 
matrix.postRotate(r, width/2, height/2); 

Trong ví dụ trên mLoc là một Location trả về bởi một nhà cung cấp gps và getBearing trả về số độ đông bắc hướng hiện tại của du lịch. item.mHeading đã được tính toán bằng cách sử dụng chức năng Location.bearingTo() sử dụng mLoc và vị trí của mục. chiều rộng và chiều cao là kích thước của chế độ xem hình ảnh.

Vì vậy, hãy đảm bảo các biến của bạn ở độ và không phải là radian, và thử 'normalizing' (nhận tiêu đề vào khoảng 0-360 và không phải -180-180). Ngoài ra, nếu kết quả được tắt 180 độ, hãy chắc chắn rằng bạn đang nhận được mangĐối với mục tiêu của bạn, chứ không phải là mức độ từ mục tiêu của bạn cho bạn.

Ma trận trên sau đó có thể được đặt trong một ImageView mà có một ScaleType.Matrix

imageView.setMatrix(matrix); 
imageview.setScaleType(ScaleType.Matrix); 

Vì bạn đang quay xung quanh điểm trung tâm của IMAGExem (chiều rộng/2, chiều cao/2 trong postRotate), drawable của bạn nên được trỏ lên trên và sẽ được xoay tại thời gian vẽ, thay vì tái tạo một bitmap mới mỗi lần.

+0

Cảm ơn bạn đã trả lời FunkTheMonk, tôi không biết về Ma trận; Tôi se thử no! Tuy nhiên tôi đã chọn câu trả lời của Chris dưới đây như là một trong những chính xác (do đó trao cho anh ta tiền thưởng). Cảm ơn một lần nữa vì sự giúp đỡ của bạn. :) – ninetwozero

1

Tôi đã dành khoảng 40 giờ một ngày cuối tuần để thực hiện việc này.

Đau ở mông, hy vọng tôi có thể tha cho bạn nỗi đau đó.

Ok, tôi cảnh báo bạn, đây là một số mã xấu. Tôi đã ở trong một pinch để hoàn thành nó, nó không có đề án đặt tên, nhưng tôi đã cố gắng để bình luận nó là tốt nhất có thể cho bạn.

Nó được sử dụng để xác định vị trí cọc lớn các hạt đặt ra trong lĩnh vực lưu trữ

Sử dụng điện thoại vĩ độ hiện tại và kinh độ, lat/lon của đích, cảm biến la bàn, và một số đại số, tôi đã có thể để tính hướng đến đích.

Lat/lon và cảm biến đo được lấy ra từ các lớp MainApplication

Đây là một số các mã cho arrow.class, mà tôi sử dụng để vẽ một mũi tên trên một canvas hướng tới một hướng.

//The location you want to go to// 
    //"Given North" 
    double lat=0; 
    double lon=0; 
    ////////////////////////////////// 
    protected void onDraw(Canvas canvas) { 

    //Sensor values from another class managing Sensor 
    float[] v = MainApplication.getValues(); 

    //The current location of the device, retrieved from another class managing GPS 
    double ourlat= MainApplication.getLatitudeD(); 
    double ourlon= MainApplication.getLongitudeD(); 

    //Manually calculate the direction of the pile from the device 
    double a= Math.abs((lon-ourlon)); 
    double b= Math.abs((lat-ourlat)); 
    //archtangent of a/b is equal to the angle of the device from 0-degrees in the first quadrant. (Think of a unit circle) 
    double thetaprime= Math.atan(a/b); 
    double theta= 0; 

    //Determine the 'quadrant' that the desired location is in 
    //ASTC (All, Sin, Tan, Cos) Determines which value is positive 
    //Gotta love Highschool algebra 

    if((lat<ourlat)&&(lon>ourlon)){//-+ 
     //theta is 180-thetaprime because it is in the 2nd quadrant 
     theta= ((Math.PI)-thetaprime); 

     //subtract theta from the compass value retrieved from the sensor to get our final direction 
     theta=theta - Math.toRadians(v[0]); 

    }else if((lat<ourlat)&&(lon<ourlon)){//-- 
     //Add 180 degrees because it is in the third quadrant 
     theta= ((Math.PI)+thetaprime); 

     //subtract theta from the compass value retreived from the sensor to get our final direction 
     theta=theta - Math.toRadians(v[0]); 

    }else if((lat>ourlat)&&(lon>ourlon)){ //++ 
     //No change is needed in the first quadrant 
     theta= thetaprime; 

     //subtract theta from the compass value retreived from the sensor to get our final direction 
     theta=theta - Math.toRadians(v[0]); 

    }else if((lat>ourlat)&&(lon<ourlon)){ //+- 
     //Subtract thetaprime from 360 in the fourth quadrant 
     theta= ((Math.PI*2)-thetaprime); 

     //subtract theta from the compass value retreived from the sensor to get our final direction 
     theta=theta - Math.toRadians(v[0]); 

    } 

    canvas.drawBitmap(_bitmap, 0, 0, paint); 
    float[] results = {0}; //Store data 
    Location.distanceBetween(ourlat, ourlon, lat, lon, results); 
    try{ 

     //Note, pileboundary is a value retreived from a database 
     //This changes the color of the canvas based upon how close you are to the destination 
     //Green < 100 (or database value), Yellow < (100)*2, Otherwise red 
     if((results[0])<(pileboundary==0?100:pileboundary)){ 
      _canvas.drawColor(Color.GREEN); 
     }else if((results[0])<(pileboundary==0?100:pileboundary)*2){ 
      _canvas.drawColor(Color.YELLOW); 
     }else{ 
      _canvas.drawColor(Color.rgb(0xff, 113, 116)); //RED-ish 
     } 
     //Draw the distance(in feet) from the destination 
     canvas.drawText("Distance: "+Integer.toString((int) (results[0]*3.2808399))+ " Feet", 3, height-3, textpaint); 
    }catch(IllegalArgumentException ex){ 
     //im a sloppy coder 
    } 
    int w = canvas.getWidth(); 
    int h = height; 
    int x = w/2; //put arrow in center 
    int y = h/2; 
    canvas.translate(x, y); 
    if (v != null) { 

     // Finally, we rotate the canvas to the desired direction 
     canvas.rotate((float)Math.toDegrees(theta)); 


    } 
    //Draw the arrow! 
    canvas.drawPath(thearrow, paint); 
} 


//Some of my declarations, once again sorry :P 
GeomagneticField gf; 
Bitmap _bitmap; 
Canvas _canvas; 
int _height; 
int _width; 
Bitmap b; 
@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    //Get the current GeomagneticField (Should be valid until 2016, according to android docs) 
    gf = new GeomagneticField((float)lat,(float)lon,(float)MainApplication.getAltitude(),System.currentTimeMillis()); 
    _height = View.MeasureSpec.getSize(heightMeasureSpec); 
    _width = View.MeasureSpec.getSize(widthMeasureSpec); 
    setMeasuredDimension(_width, _height); 
    _bitmap = Bitmap.createBitmap(_width, _height, Bitmap.Config.ARGB_8888); 
    _canvas = new Canvas(_bitmap); 
    b=Bitmap.createBitmap(_bitmap); 
    drawBoard(); 
    invalidate(); 
} 


//Here is the code to draw the arrow 
    thearrow.moveTo(0, -50); 
    thearrow.lineTo(-20, 50); 
    thearrow.lineTo(0, 50); 
    thearrow.lineTo(20, 50); 
    thearrow.close(); 
    thearrow.setFillType(FillType.EVEN_ODD); 

Hy vọng bạn có thể quản lý để đọc mã của tôi ... Nếu tôi có thời gian, tôi sẽ làm cho nó đẹp hơn một chút.

Nếu bạn cần bất kỳ giải thích nào, hãy cho tôi biết.

-MrZander

+1

Bạn biết không ... tôi chỉ nhìn vào tên của bạn ... bạn đã làm cho ứng dụng Batt3elog BF3. Tôi đã thực hiện truy xuất số liệu thống kê BF3. Tôi chỉ giúp đối thủ của mình: P – MrZander

+0

Xin chào Alexander, cảm ơn vì đã chia sẻ mã. Tuy nhiên, tôi đã chọn câu trả lời của Chris khi anh ấy thực sự giúp tôi ngoài Stackoverflow, nhưng tôi đã nói với anh ấy để đăng giải pháp trên trang web để tôi có thể đánh dấu câu trả lời của anh ấy là "câu trả lời" để tham khảo thêm. Tôi đánh giá cao bạn đã dành thời gian của bạn mặc dù, vì tôi sẽ được học một hoặc hai điều từ bài viết của bạn. :-) – ninetwozero

+1

Và có, nó khá buồn cười mà chúng tôi sẽ gặp ở đây của tất cả các nơi quá. :-) – ninetwozero

Các vấn đề liên quan