Círculo de dibujo de Android Maps API v2

Tener el siguiente código para dibujar el círculo (tomado de la muestra de “mapas” 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)); 

Esto es lo que se dibuja en diferentes partes del mundo:

SydneyScandic

Como ves, los círculos no son realmente círculos e incluso el segundo es básicamente una elipse.

Supongo que el “anti-aliasing” del círculo depende del número de puntos en la variable int numPoints .

  1. ¿Qué es la variable int radius = 5 en el código de ejemplo? Quiero decir, ¿qué medida es?
  2. Y la pregunta principal ¿cuál sería la forma correcta de dibujar un buen círculo con un radio dado en metros ? Algo muy similar a lo que teníamos en api v1 con canvas.drawCircle()

ACTUALIZAR ——————–

OK después de mejorar las matemáticas, pude dibujar un círculo “a la derecha”:

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

Sin embargo, supongo que el anti-aliasing del círculo está fuera de control, y en algunos niveles de zoom el círculo puede ponerse feo:

enter image description here

Cómo dibujar un círculo en 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(px, py + 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); 

y resultado:

google maps v2 dibujar un círculo

Google made es simple en los mapas v2. El siguiente fragmento muestra marcadores de dibujo y círculos junto con la actualización de sus posiciones.

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

Android 2.0 API puede usar CircleOption para dibujar un círculo, funciona realmente bien, sin conversiones desordenadas de píxeles a metros.

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

Aquí está mi código, solo para la variación agregada. No he encontrado una manera de resolver los problemas con antialiasing o simplificación de formas:

 // 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 makeCircle(LatLng centre, double radius) { ArrayList points = new ArrayList(); 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; } 

Explicación de las matemáticas

Google Earth usa la proyección de Mercator, lo que significa que los círculos físicos no aparecen como círculos en el mapa, sino que están distorsionados, como se muestra en la pregunta. Cuanto más cerca de los polos vas, más distorsionados están. Para dibujar un círculo en el mapa pero con coordenadas dadas en latitud / longitud, necesitamos invertir la distorsión.

Primero, encontramos el centro del círculo en radianes, que es bastante simple y se almacena en lat y lon .

Luego damos la vuelta al círculo encontrando puntos en su borde. Si estuviéramos haciendo un círculo físico, la latitud y longitud de cada punto es justa:

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

Esa es una trigonometría bastante simple. El (radio / EARTH_RADIUS) es el radio angular del círculo en radianes (por ejemplo, un círculo con un radio de 100 m tendría un radio angular de 0.000015 rad).

Si usáramos ese código simple, encontraríamos que el círculo que se muestra en el mapa fue aplastado por cos(lat) horizontalmente, por lo que simplemente lo dividimos por cos(lat) para corregirlo.

Igualmente podríamos haber hecho esto:

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

Resultando en un círculo grande: depende de si el parámetro de función de radius se refiere a la latitud o longitud.

Esta explicación podría usar algunos diagtwigs, pero no puedo molestarme, lo siento.

Google hoy, 26 de febrero, acaba de lanzar Circle como un tipo de superposición.

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

Ayer, el equipo de desarrolladores de Android introdujo el servicio v3 de Google Play. ¡Abusar de ello! 😉 https://developers.google.com/maps/documentation/android/shapes#circles