2012-12-21 47 views
14

具有下列代碼繪製圓(從谷歌採取的播放服務「地圖」樣品):Android地圖API第2戰平圈

PolylineOptions options = new PolylineOptions(); 
    int radius = 5; //What is that? 
    int numPoints = 100; 
    double phase = 2 * Math.PI/numPoints; 
    for (int i = 0; i <= numPoints; i++) { 
     options.add(new LatLng(SYDNEY.latitude + radius * Math.sin(i * phase), 
       SYDNEY.longitude + radius * Math.cos(i * phase))); 
    } 
    int color = Color.RED; 
    mMap.addPolyline(options 
      .color(color) 
      .width(2)); 

這是被畫在世界的不同部分:

Sydney Scandic

正如你所看到的圓圈不是真的圓圈,甚至第二個橢圓基本上是圓的。

我猜圓圈的「反鋸齒」取決於int numPoints變量中的點數。

  1. 什麼是示例代碼中的變量int radius = 5?我的意思是它是什麼措施?
  2. 和主要問題什麼是正確的方式繪製在給定半徑的漂亮圓圈?東西smiliar什麼,我們在API第1版曾與canvas.drawCircle()

UPDATE --------------------我

OK提高數學後能得出「正確」的圈子:

private void addCircle(LatLng latLng, double radius) 
    { 
     double R = 6371d; // earth's mean radius in km 
     double d = radius/R; //radius given in km 
     double lat1 = Math.toRadians(latLng.latitude); 
     double lon1 = Math.toRadians(latLng.longitude);   
     PolylineOptions options = new PolylineOptions(); 
     for (int x = 0; x <= 360; x++) 
     {      
      double brng = Math.toRadians(x); 
      double latitudeRad = Math.asin(Math.sin(lat1)*Math.cos(d) + Math.cos(lat1)*Math.sin(d)*Math.cos(brng)); 
      double longitudeRad = (lon1 + Math.atan2(Math.sin(brng)*Math.sin(d)*Math.cos(lat1), Math.cos(d)-Math.sin(lat1)*Math.sin(latitudeRad)));    
      options.add(new LatLng(Math.toDegrees(latitudeRad), Math.toDegrees(longitudeRad))); 
     }   
     mMap.addPolyline(options.color(Color.BLACK).width(2));   
    } 

然而圈我猜的抗鋸齒有些無法控制,並在一些縮放級別循環可能會難看:

enter image description here

+3

你得到橢圓它的原因是因爲對數學的'options.add('是非常基本的,假裝地球是一個平坦的表面,就像哥白尼之前,如果你畫到較小的區域,你如果你想要一個精確的圓圈,你可以將這個數學擴展到適當考慮地球的形狀 – Budius

+0

@lija,任何使用「多段線」而不是「多邊形」來繪製的特定原因圓形?我嘗試使用Polygon並獲得相同的輸出。因此,您有任何想法可以使用它們中的哪一個?Thank You。 –

+0

@ Archie.bpgc不是真的,如果您需要使用多邊形,可以使用多邊形例如填充選項,否則在這種情況下沒有太大的區別。 –

回答

20

如何繪製圓谷歌地圖V2(位圖)

// 1. some variables: 

    private static final double EARTH_RADIUS = 6378100.0; 
    private int offset; 

// 2. convert meters to pixels between 2 points in current zoom: 

    private int convertMetersToPixels(double lat, double lng, double radiusInMeters) { 

     double lat1 = radiusInMeters/EARTH_RADIUS; 
     double lng1 = radiusInMeters/(EARTH_RADIUS * Math.cos((Math.PI * lat/180))); 

     double lat2 = lat + lat1 * 180/Math.PI; 
     double lng2 = lng + lng1 * 180/Math.PI; 

     Point p1 = YourActivity.getMap().getProjection().toScreenLocation(new LatLng(lat, lng)); 
     Point p2 = YourActivity.getMap().getProjection().toScreenLocation(new LatLng(lat2, lng2)); 

     return Math.abs(p1.x - p2.x); 
    } 

// 3. bitmap creation: 

    private Bitmap getBitmap() { 

     // fill color 
     Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG); 
     paint1.setColor(0x110000FF); 
     paint1.setStyle(Style.FILL); 

     // stroke color 
     Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG); 
     paint2.setColor(0xFF0000FF); 
     paint2.setStyle(Style.STROKE); 

     // icon 
     Bitmap icon = BitmapFactory.decodeResource(YourActivity.getResources(), R.drawable.blue); 

     // circle radius - 200 meters 
     int radius = offset = convertMetersToPixels(lat, lng, 200); 

     // if zoom too small 
     if (radius < icon.getWidth()/2) { 

      radius = icon.getWidth()/2; 
     } 

     // create empty bitmap 
     Bitmap b = Bitmap.createBitmap(radius * 2, radius * 2, Config.ARGB_8888); 
     Canvas c = new Canvas(b); 

     // draw blue area if area > icon size 
     if (radius != icon.getWidth()/2) { 

      c.drawCircle(radius, radius, radius, paint1); 
      c.drawCircle(radius, radius, radius, paint2); 
     } 

     // draw icon 
     c.drawBitmap(icon, radius - icon.getWidth()/2, radius - icon.getHeight()/2, new Paint()); 

     return b; 
    } 

// 4. calculate image offset: 

    private LatLng getCoords(double lat, double lng) { 

     LatLng latLng = new LatLng(lat, lng); 

     Projection proj = YourActivity.getMap().getProjection(); 
     Point p = proj.toScreenLocation(latLng); 
     p.set(p.x, p.y + offset); 

     return proj.fromScreenLocation(p); 
    } 

// 5. draw: 

     MarkerOptions options = new MarkerOptions(); 
      options.position(getCoords(lat, lng)); 
      options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap())); 

      marker = YourActivity.getMap().addMarker(options); 

和結果:

google maps v2 draw circle

+1

儘管你的解決方案有一些小錯誤,但我有一個主意。無論如何,使用v2 API似乎很難實現比以前更簡單的任務。例如,如果我需要在用戶拖動進度條時調整此圓圈的大小,我將不得不刪除標記並創建一個較慢的標記,同時還需要管理位圖的生命週期。所有這些在V1中使用Overlay都非常簡單。 –

+0

只需添加標記marker = map.addMarker(options.position(latLng));它工作正常 –

4

這是我的代碼,只是爲了增加變化。我還沒有找到一種方法來解決問題,抗鋸齒或形狀簡化:

// Make a circle of latitude and longitude points. 
// Radius is in metres. 
// Probably won't work if the circle is large or near the poles. 
private ArrayList<LatLng> makeCircle(LatLng centre, double radius) 
{ 
    ArrayList<LatLng> points = new ArrayList<LatLng>(); 

    double EARTH_RADIUS = 6378100.0; 
    // Convert to radians. 
    double lat = centre.latitude * Math.PI/180.0; 
    double lon = centre.longitude * Math.PI/180.0; 

    for (double t = 0; t <= Math.PI * 2; t += 0.1) 
    { 
     // y 
     double latPoint = lat + (radius/EARTH_RADIUS) * Math.sin(t); 
     // x 
     double lonPoint = lon + (radius/EARTH_RADIUS) * Math.cos(t)/Math.cos(lat); 
     points.add(new LatLng(latPoint * 180.0/Math.PI, lonPoint * 180.0/Math.PI)); 
    } 

    return points; 
} 

數學

的說明

谷歌地球使用Mercator projection這意味着物理界不會出現在地圖上圈 - 相反,他們被扭曲,如問題所示。越靠近極點,你越扭曲他們。要在地圖上繪製一個圓,但座標以緯度/經度給出,我們需要反轉失真。

首先我們找到圓弧的中心 - 這非常簡單,存儲在latlon中。

然後我們繞過它的邊緣找到點。如果我們在製作物理圈,每個點的經緯度就是:

 double latPoint = lat + (radius/EARTH_RADIUS) * Math.sin(t); 
     double lonPoint = lon + (radius/EARTH_RADIUS) * Math.cos(t); 

這是非常簡單的三角學。 (radius/EARTH_RADIUS)是以弧度表示的圓的半徑(例如,半徑爲100m的圓的角半徑爲0.000015rad)。

如果我們使用這個簡單的代碼,我們會發現地圖上顯示的圓被水平壓扁cos(lat),所以我們簡單地除以cos(lat)來解除它。

我們同樣可以這樣做:

 double latPoint = lat + (radius/EARTH_RADIUS) * Math.sin(t) * Math.cos(lat); 
     double lonPoint = lon + (radius/EARTH_RADIUS) * Math.cos(t); 

成一個大圈得到的 - 這取決於radius功能參數是否指的是經緯度。

此解釋可以使用一些圖表,但我不能打擾,對不起。

+0

你可以在這裏解釋數學嗎? – Sulby

+0

我添加了一些解釋。 – Timmmm

7

Android 2.0 API可以使用CircleOption繪製一個圓,效果很好,不會像素轉換爲米。

public CircleOptions getCircleOptions() { 
    CircleOptions co = new CircleOptions(); 
    co.center(latlng); 
    co.radius(getCircleSize()); 
    co.fillColor(getCircleColor()); 
    co.strokeColor(getStrokeColor()); 
    co.strokeWidth(2.0f); 
    return co; 
} 

    Circle circle = mapView.getMap().addCircle(munzeeMarker.getCircleOptions()); 
21

Google製作的地圖很簡單v2。以下片段演示了繪圖標記和圓以及一起更新它們的位置。

private Circle mCircle; 
private Marker mMarker; 
private GoogleMap mGoogleMap; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    mGoogleMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.mapFragment)).getMap(); 
    mGoogleMap.setMyLocationEnabled(true); 
    mGoogleMap.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() { 
     @Override 
     public void onMyLocationChange(Location location) { 
      LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude()); 
      if(mCircle == null || mMarker == null){ 
       drawMarkerWithCircle(latLng); 
      }else{ 
       updateMarkerWithCircle(latLng); 
      } 
     } 
    }); 
} 

private void updateMarkerWithCircle(LatLng position) { 
    mCircle.setCenter(position); 
    mMarker.setPosition(position); 
} 

private void drawMarkerWithCircle(LatLng position){ 
    double radiusInMeters = 100.0; 
    int strokeColor = 0xffff0000; //red outline 
    int shadeColor = 0x44ff0000; //opaque red fill 

    CircleOptions circleOptions = new CircleOptions().center(position).radius(radiusInMeters).fillColor(shadeColor).strokeColor(strokeColor).strokeWidth(8); 
    mCircle = mGoogleMap.addCircle(circleOptions); 

    MarkerOptions markerOptions = new MarkerOptions().position(position); 
    mMarker = mGoogleMap.addMarker(markerOptions); 
}