2012-12-21 10 views
14

den folgenden Code Mit Kreis zu zeichnen (aus Google Play-Dienste "Maps" Probe):Android Maps API v2 draw Kreis

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)); 

Dies ist, was auf anderen Teil der Welt gezeichnet wird:

Sydney Scandic

Wie Sie Kreise sehen, sind nicht wirklich Kreise und sogar zweite ist Ellipse im Grunde.

Ich denke, dass "Anti-Aliasing" von Kreis abhängig von der Anzahl der Punkte in int numPoints Variable.

  1. Was ist int radius = 5 Variable in Beispielcode? Ich meine, welches Maß es ist?
  2. Und Hauptfrage, was wäre die richtige Art der Zeichnung schönen Kreis mit gegebenem Radius in Meter? Etwas smiliar zu dem, was wir mit canvas.drawCircle()

UPDATE --------------------

OK, nachdem die Verbesserung der Mathematik in api v1 hatte konnte ich ziehen „rechts“ Kreis:

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));   
    } 

jedoch Anti-Aliasing des Kreises ich denke, ist etwas außer Kontrolle, und auf einig Zoomstufen Kreis könnte hässlich:

enter image description here

+3

der Grund, warum Sie Ellipse sind immer es ist, weil die Mathematik auf die 'options.add (' ist sehr einfach und gibt vor, die Erde eine flache Oberfläche ist, genau wie vor Kopernikus. Wenn Sie es auf kleineren Flächen zeichnen Sie Wenn Sie einen präzisen Kreis haben wollen, müssen Sie die Mathematik so erweitern, dass sie die Form der Erde berücksichtigt. – Budius

+0

@lija, Irgendein bestimmter Grund, eine Polylinie anstelle von Polygon zu verwenden der Kreis? Ich habe versucht, mit Polygon und ich habe die gleiche Ausgabe. Also, Sie haben eine Idee, welche von ihnen ist am besten zu verwenden? Vielen Dank. –

+0

@ Archie.bpgc nicht wirklich, könnten Sie Polygone verwenden, wenn Sie eine benötigen Fülloption zum Beispiel, sonst ist das in diesem Fall kein großer Unterschied –

Antwort

20

Wie Kreis in Google Maps v2 (Bitmap)

// 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); 

und Ergebnis ziehen:

google maps v2 draw circle

+1

Obwohl Ihre Lösung kleinere Fehler hat, habe ich eine Idee. Wie auch immer, mit v2 API scheint es, als wären triviale Aufgaben viel schwieriger zu implementieren als vorher. Zum Beispiel, wenn ich diesen Kreis ändern müsste, wenn der Benutzer den Fortschrittsbalken zieht, müsste ich den Marker entfernen und einen neuen erstellen, der langsam ist, außerdem müsste ich den Lebenszyklus der Bitmap verwalten. All dies war sehr einfach mit Overlay in V1. –

+0

nur zum Hinzufügen Marker marker = map.addMarker (options.position (latLng)); es funktioniert gut –

4

Hier ist mein Code, nur für zusätzliche Variation.

// 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; 
} 

Erklärung der Mathematik

Google Earth verwendet die Mercator projection was bedeutet, dass physische Kreise erscheinen nicht als Kreise auf der Karte -: Ich habe nicht einen Weg, um die Probleme zu lösen mit Anti-Aliasing oder Form Vereinfachung gefunden stattdessen sind sie wie in der Frage gezeigt verzerrt. Je näher du an den Polen bist, desto verzerrter sind sie. Um einen Kreis auf der Karte zu zeichnen, aber mit Koordinaten in Breite/Länge, müssen wir die Verzerrung umkehren.

Zuerst finden wir den Mittelpunkt des Kreises im Bogenmaß - das ist ziemlich einfach und wird in lat und lon gespeichert.

Dann gehen wir um den Kreis, der Punkte auf seiner Kante findet. Wenn wir einen physischen Kreis zu machen, ist die Breite und Länge von jedem Punkt nur:

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

Das ist ziemlich einfach Trigonometrie ist. Der (Radius/EARTH_RADIUS) ist der Winkel Radius des Kreises im Bogenmaß (z. B. hätte ein Kreis mit einem Radius von 100 m einen Winkelradius von 0,000015 rad).

Wenn wir diesen einfachen Code verwenden, würden wir finden, dass der auf der Karte gezeigte Kreis horizontal durch cos(lat) gequetscht wurde, also teilen wir ihn einfach durch cos(lat), um ihn zu beseitigen.

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

Resultierende in einem großen Kreis - es hängt davon ab, ob die radius Funktionsparameter zur Breite oder Länge bezieht:

Wir könnten ebenso dies getan haben.

Diese Erklärung könnte einige Diagramme verwenden, aber ich kann nicht belästigt werden, sorry.

+0

Können Sie die Mathematik hier erklären? – Sulby

+0

Ich habe ein bisschen Erklärung hinzugefügt. – Timmmm

1

Gestern Android Developer Team intoduced Google-Service v3 spielen. Missbrauche es! ;)

7

Android 2.0 API können Sie CircleOption verwenden, um einen Kreis zu zeichnen, funktioniert wirklich gut, keine unordentlichen Konvertierungen von Pixeln zu Metern.

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 gemacht ist einfach in Karten v2. Der folgende Ausschnitt zeigt sowohl das Zeichnen von Markierungen und Kreisen als auch das Aktualisieren ihrer Positionen zusammen.

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); 
} 
Verwandte Themen