Подтвердить что ты не робот

API-интерфейс API Android v2

Имея следующий код для рисования круга (взятый из карты 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));

Это то, что нарисовано в разных частях мира:

SydneyScandic

Как вы видите, круги не являются кругами, а второй - эллипсом.

Я предполагаю, что "сглаживание" круга зависит от количества точек в переменной int numPoints.

  • Что такое int radius = 5 variable в примере кода? Я имею в виду, что это за показатель?
  • И главный вопрос, что было бы правильным способом рисования красивого круга с заданным радиусом в метрах? Что-то знакомое нам в api v1 с canvas.drawCircle()

ОБНОВЛЕНИЕ --------------------

ОК после улучшения математики я смог нарисовать "правый" круг:

private void addCircle(LatLng latLng, double radius)
    {
        double R = 6371d; // earth 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));          
    }

Однако сглаживание круга, на мой взгляд, несколько не поддается контролю, а на некоторых уровнях масштабирования кружок может стать уродливым:

enter image description here

4b9b3361

Ответ 1

Как нарисовать круг в Картах Google v2 (растровое изображение)

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

и результат:

google maps v2 draw circle

Ответ 2

Google сделал простую версию на картах v2. В приведенном ниже фрагменте показаны как чертежные маркеры, так и круги вместе с обновлением их позиций.

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

Ответ 3

Android 2.0 API вы можете использовать CircleOption, чтобы нарисовать круг, отлично работает, без грязных преобразований пикселей в метры.

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

Ответ 4

Здесь мой код, просто для добавления изменений. Я не нашел способ решить проблемы с сглаживанием или упрощением формы:

// 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 использует прогноз Меркатора, что означает, что физические круги не отображаются в виде кругов на карте - вместо этого они искажаются, как показано в вопрос. Чем ближе к полюсам вы идете, тем они более искажены. Чтобы нарисовать круг на карте, но с координатами, заданными в широте/долготе, нам нужно инвертировать искажение.

Сначала мы находим центр круга в радианах - это довольно просто и хранится в lat и lon.

Затем мы обходим круг, находящий точки на его ребре. Если бы мы делали физический круг, широта и долгота каждой точки были бы следующими:

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

Это довольно простая тригонометрия. Радиус (радиус/EARTH_RADIUS) является радиусом angular окружности в радианах (например, круг с радиусом 100 м имеет радиус angular 0,000015 рад).

Если бы мы использовали этот простой код, мы увидели бы, что круг, показанный на карте, был скошен на cos(lat) по горизонтали, поэтому мы просто делим на cos(lat), чтобы его не найти.

Мы могли бы также сделать это:

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

Результат в большом круге - зависит от того, относится ли параметр функции radius к широте или долготе.

Это объяснение может использовать некоторые диаграммы, но я не могу быть обеспокоен, извините.