web-dev-qa-db-fra.com

Cercle de dessin de l’API Android Maps v2

Avoir le code suivant pour dessiner un cercle (extrait de l'exemple "maps" de Google Play Services):

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

Voici ce qui est attiré par différentes parties du monde:

SydneyScandic

Comme vous le voyez, les cercles ne sont pas vraiment des cercles et même le second est fondamentalement une ellipse.

Je suppose que "l'anti-aliasing" du cercle dépend du nombre de points dans la variable int numPoints.

  1. Qu'est-ce que la variable int radius = 5 dans l'exemple de code? Je veux dire quelle mesure c'est?
  2. Et question principale: quelle serait la bonne façon de tracer un cercle de Nice avec un rayon donné en mètres ? Quelque chose de positif à ce que nous avions dans api v1 avec canvas.drawCircle()

METTRE À JOUR --------------------

OK, après avoir amélioré les mathématiques, j'ai pu dessiner le cercle "correct":

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

L'anti-aliasing du cercle, je suppose, est un peu incontrôlable et, sur certains niveaux de zoom, le cercle peut devenir moche

enter image description here

16
Ilja S.

Comment dessiner un cercle dans 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);

et résultat:

google maps v2 draw circle

21
embo

Google made est simple dans maps v2. L'extrait ci-dessous illustre à la fois les marques de dessin et les cercles et met à jour leurs positions ensemble. 

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

Android 2.0 API, vous pouvez utiliser CircleOption pour dessiner un cercle, fonctionne vraiment bien, pas de conversions désordonnées de pixels en mètres. 

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

Voici mon code, juste pour une variation supplémentaire. Je n'ai pas trouvé le moyen de résoudre les problèmes d'antialiasing ou de simplification de forme:

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

Explication des maths

Google Earth utilise la projection Mercator , ce qui signifie que les cercles physiques n'apparaissent pas sous forme de cercles sur la carte - ils sont déformés, comme indiqué dans la question. Plus vous vous approchez des pôles, plus ils sont déformés. Pour dessiner un cercle sur la carte mais avec les coordonnées données en latitude/longitude, nous devons inverser la distorsion.

Nous trouvons d’abord le centre du cercle en radians - c’est assez simple et est stocké dans lat et lon.

Ensuite, nous contournons le cercle pour trouver des points sur son bord. Si nous faisions un cercle physique, la latitude et la longitude de chaque point sont simplement:

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

C'est une trigonométrie assez simple. Le (rayon/EARTH_RADIUS) est le rayon angulaire du cercle en radians (par exemple, un cercle de rayon 100 m aurait un rayon angulaire de 0,000015 rad).

Si nous utilisions ce simple code, nous constaterions que le cercle indiqué sur la carte était écrasé par cos(lat) horizontalement. Nous divisons donc simplement par cos(lat) pour le supprimer.

Nous aurions également pu faire ceci:

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

Le résultat est un grand cercle - cela dépend si le paramètre de la fonction radius fait référence à la latitude ou à la longitude.

Cette explication pourrait utiliser certains diagrammes mais je ne peux pas être dérangé, désolé.

4
Timmmm

Le 26 février, Google vient de publier Circle en tant que type de superposition. 

https://developers.google.com/maps/documentation/Android/reference/com/google/Android/gms/maps/model/Circle

2
Jeff Arenberg

Hier, l'équipe de développeurs Android a introduit le service Google Play v3. Abuse! ;) https://developers.google.com/maps/documentation/Android/shapes#circles

1
zmeda