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:
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
.
int radius = 5
dans l'exemple de code? Je veux dire quelle mesure c'est?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
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 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);
}
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());
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;
}
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é.
Le 26 février, Google vient de publier Circle en tant que type de superposition.
Hier, l'équipe de développeurs Android a introduit le service Google Play v3. Abuse! ;) https://developers.google.com/maps/documentation/Android/shapes#circles