wake-up-neo.com

Android Maps API v2-Zeichnungskreis

Mit dem folgenden Code zum Zeichnen eines Kreises (aus dem Google Maps-Beispiel für "Karten" entnommen):

    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 wird von verschiedenen Teilen der Welt angezogen:

SydneyScandic

Wie Sie sehen, sind Kreise keine wirklichen Kreise und im Grunde ist es sogar eine zweite Ellipse.

Ich vermute, dass "Anti-Aliasing" des Kreises abhängig von der Anzahl der Punkte in int numPoints variabel ist.

  1. Was ist int radius = 5 Variable im Beispielcode? Ich meine, was ist das für ein Maß?
  2. Und die Hauptfrage, was wäre der richtige Weg, um einen Nizza-Kreis mit dem angegebenen Radius in Metern zu zeichnen? Etwas, das wir in api v1 mit canvas.drawCircle() hatten

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

OK, nachdem ich die Mathematik verbessert hatte, konnte ich den "richtigen" Kreis zeichnen:

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

Ich vermute jedoch, dass das Anti-Aliasing des Kreises etwas unkontrollierbar ist, und auf einige Zoomstufen können hässlich werden:

enter image description here

16
Ilja S.

Wie zeichnet man einen 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:

google maps v2 draw circle

21
embo

Google made ist in Maps v2 einfach. Das folgende Snippet zeigt sowohl das Zeichnen von Markierungen als auch Kreise sowie das Aktualisieren ihrer Positionen. 

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

Mit der Android 2.0-API können Sie mit CircleOption einen Kreis zeichnen, funktionieren wirklich gut, keine unordentlichen Konvertierungen von Pixel in 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());
7
Chrispix

Hier ist mein Code, nur für zusätzliche Variationen. Ich habe keinen Weg gefunden, die Probleme mit Antialiasing oder Formvereinfachung zu lösen:

// 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-Projektion , was bedeutet, dass physische Kreise nicht als Kreise auf der Karte angezeigt werden, sondern wie in der Frage gezeigt verzerrt. Je näher Sie an den Polen sind, desto verzerrter sind sie. Um einen Kreis auf der Karte zu zeichnen, wobei die Koordinaten in Längen- und Breitengrad angegeben sind, 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 herum und finden Punkte an seinem Rand. Wenn wir einen physischen Kreis bilden, ist der Längen- und Breitengrad jedes Punktes nur:

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

Das ist ziemlich einfache Trigonometrie. Der (Radius/EARTH_RADIUS) ist der Winkel Radius des Kreises im Bogenmaß (z. B. würde ein Kreis mit einem Radius von 100 m einen Winkelradius von 0,000015 Rad haben).

Wenn wir diesen einfachen Code verwenden, würden wir feststellen, dass der auf der Karte dargestellte Kreis horizontal von cos(lat) gequetscht wurde. Daher teilen wir uns einfach durch cos(lat) auf, um ihn zu löschen.

Das hätten wir auch tun können:

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

Ergebnis in einem großen Kreis - es hängt davon ab, ob sich der Funktionsparameter radius auf Breitengrad oder Längengrad bezieht.

Diese Erklärung könnte einige Diagramme verwenden, aber es macht mir nichts aus, sorry.

4
Timmmm
2
Jeff Arenberg

Gestern stellte das Android Developer Team den Google Play Service v3 vor. Missbrauch es! ;) https://developers.google.com/maps/documentation/Android/shapes#circles

1
zmeda