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

Android: Как установить уровень масштабирования вида карты в радиусе 1 км вокруг моего текущего местоположения?

Я хочу, чтобы изображение было увеличено до радиуса 1 км, но не могу понять, как?

Док говорит, что уровень масштабирования 1 будет отображать экваториальные экваторы на 256 пикселей. Итак, как я могу рассчитать, какой уровень масштабирования мне нужно установить, чтобы вид карты отображал область в радиусе 1KM?

UPDATE:
Прочитав несколько сообщений в блоге, я написал следующий код:

private int calculateZoomLevel() {
    double equatorLength = 6378140; // in meters
    double widthInPixels = screenWidth;
    double metersPerPixel = equatorLength / 256;
    int zoomLevel = 1;
    while ((metersPerPixel * widthInPixels) > 2000) {
        metersPerPixel /= 2;
        ++zoomLevel;
    }
    Log.i("ADNAN", "zoom level = "+zoomLevel);
    return zoomLevel;
}

Идея состоит в том, что сначала я вычисляю Метры на пиксель на уровне масштабирования 1, который согласно google показывает экватор земли с использованием 256 пикселей. Теперь каждый последующий уровень увеличения увеличивается на уровень 2, поэтому я половину метров на пиксель для каждого уровня масштабирования. Я делаю это, пока у меня не будет уровня масштабирования, где метры на пиксель, умноженные на ширину экрана , дает мне меньше 2000 i.e 2 км в поперечнике.

Но я не думаю, что уровень масштабирования, который я получаю, показывает карту радиуса 2Km. Может кто-нибудь сказать мне, что я делаю неправильно здесь?

4b9b3361

Ответ 1

Следующий код - это то, что в конечном итоге используется. Учитывая ширину экрана и тот факт, что при уровне масштабирования 1 экватор Земли имеет длину 256 пикселей, а каждый последующий уровень масштабирования удваивает количество пикселей, необходимых для представления экватора Земли, следующая функция возвращает уровень масштабирования, где на экране будет отображаться область ширины 2Km.

private int calculateZoomLevel(int screenWidth) {
    double equatorLength = 40075004; // in meters
    double widthInPixels = screenWidth;
    double metersPerPixel = equatorLength / 256;
    int zoomLevel = 1;
    while ((metersPerPixel * widthInPixels) > 2000) {
        metersPerPixel /= 2;
        ++zoomLevel;
    }
    Log.i("ADNAN", "zoom level = "+zoomLevel);
    return zoomLevel;
}

Ответ 2

хотя этот ответ логичен, и я нахожу его работающим, но результаты не точны, я не знаю, почему, но я устал от этого подхода, и этот метод намного точнее.

1) Сделайте круг на объекте с желаемым радиусом

Circle circle = mGoogleMap.addCircle(new CircleOptions().center(new LatLng(latitude, longitude)).radius(getRadiusInMeters()).strokeColor(Color.RED));           
        circle.setVisible(true);
        getZoomLevel(circle);

2) Передайте этот объект этой функции и установите уровень масштабирования Здесь ссылка

public int getZoomLevel(Circle circle) {
if (circle != null){
    double radius = circle.getRadius();
    double scale = radius / 500;
    zoomLevel =(int) (16 - Math.log(scale) / Math.log(2));
}
return zoomLevel;
}

Ответ 3

В итоге я использовал utils из:

https://github.com/googlemaps/android-maps-utils

Я извлек класс из lib, поэтому вам не нужна вся библиотека. Вместо установки уровня масштабирования вы используете границы. Результат тот же.

Код, чтобы показать ровно 1 километр:

animateToMeters(1000);

private void animateToMeters(int meters){
    int mapHeightInDP = 200;
    Resources r = getResources();
    int mapSideInPixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mapHeightInDP, r.getDisplayMetrics());

    LatLng point = new LatLng(0, 0);
    LatLngBounds latLngBounds = calculateBounds(point, meters);
    if(latLngBounds != null){
        cameraUpdate = CameraUpdateFactory.newLatLngBounds(latLngBounds, mapSideInPixels, mapSideInPixels, MARKER_BOUNDS);
        if(mMap != null)
            mMap.animateCamera(cameraUpdate); 
    }
}

private LatLngBounds calculateBounds(LatLng center, double radius) {
    return new LatLngBounds.Builder().
      include(SphericalUtil.computeOffset(center, radius, 0)).
      include(SphericalUtil.computeOffset(center, radius, 90)).
      include(SphericalUtil.computeOffset(center, radius, 180)).
      include(SphericalUtil.computeOffset(center, radius, 270)).build();
}

Класс, извлеченный (слегка измененный) из lib:

public class SphericalUtil {

    static final double EARTH_RADIUS = 6371009;

    /**
     * Returns hav() of distance from (lat1, lng1) to (lat2, lng2) on the unit sphere.
     */
    static double havDistance(double lat1, double lat2, double dLng) {
        return hav(lat1 - lat2) + hav(dLng) * cos(lat1) * cos(lat2);
    }

    /**
     * Returns haversine(angle-in-radians).
     * hav(x) == (1 - cos(x)) / 2 == sin(x / 2)^2.
     */
    static double hav(double x) {
        double sinHalf = sin(x * 0.5);
        return sinHalf * sinHalf;
    }

    /**
     * Computes inverse haversine. Has good numerical stability around 0.
     * arcHav(x) == acos(1 - 2 * x) == 2 * asin(sqrt(x)).
     * The argument must be in [0, 1], and the result is positive.
     */
    static double arcHav(double x) {
        return 2 * asin(sqrt(x));
    }

    private SphericalUtil() {}

    /**
     * Returns the heading from one LatLng to another LatLng. Headings are
     * expressed in degrees clockwise from North within the range [-180,180).
     * @return The heading in degrees clockwise from north.
     */
    public static double computeHeading(LatLng from, LatLng to) {
        // http://williams.best.vwh.net/avform.htm#Crs
        double fromLat = toRadians(from.latitude);
        double fromLng = toRadians(from.longitude);
        double toLat = toRadians(to.latitude);
        double toLng = toRadians(to.longitude);
        double dLng = toLng - fromLng;
        double heading = atan2(
                sin(dLng) * cos(toLat),
                cos(fromLat) * sin(toLat) - sin(fromLat) * cos(toLat) * cos(dLng));
        return wrap(toDegrees(heading), -180, 180);
    }

    /**
     * Returns the LatLng resulting from moving a distance from an origin
     * in the specified heading (expressed in degrees clockwise from north).
     * @param from     The LatLng from which to start.
     * @param distance The distance to travel.
     * @param heading  The heading in degrees clockwise from north.
     */
    public static LatLng computeOffset(LatLng from, double distance, double heading) {
        distance /= EARTH_RADIUS;
        heading = toRadians(heading);
        // http://williams.best.vwh.net/avform.htm#LL
        double fromLat = toRadians(from.latitude);
        double fromLng = toRadians(from.longitude);
        double cosDistance = cos(distance);
        double sinDistance = sin(distance);
        double sinFromLat = sin(fromLat);
        double cosFromLat = cos(fromLat);
        double sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading);
        double dLng = atan2(
                sinDistance * cosFromLat * sin(heading),
                cosDistance - sinFromLat * sinLat);
        return new LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng));
    }

    /**
     * Returns the location of origin when provided with a LatLng destination,
     * meters travelled and original heading. Headings are expressed in degrees
     * clockwise from North. This function returns null when no solution is
     * available.
     * @param to       The destination LatLng.
     * @param distance The distance travelled, in meters.
     * @param heading  The heading in degrees clockwise from north.
     */
    public static LatLng computeOffsetOrigin(LatLng to, double distance, double heading) {
        heading = toRadians(heading);
        distance /= EARTH_RADIUS;
        // http://lists.maptools.org/pipermail/proj/2008-October/003939.html
        double n1 = cos(distance);
        double n2 = sin(distance) * cos(heading);
        double n3 = sin(distance) * sin(heading);
        double n4 = sin(toRadians(to.latitude));
        // There are two solutions for b. b = n2 * n4 +/- sqrt(), one solution results
        // in the latitude outside the [-90, 90] range. We first try one solution and
        // back off to the other if we are outside that range.
        double n12 = n1 * n1;
        double discriminant = n2 * n2 * n12 + n12 * n12 - n12 * n4 * n4;
        if (discriminant < 0) {
            // No real solution which would make sense in LatLng-space.
            return null;
        }
        double b = n2 * n4 + sqrt(discriminant);
        b /= n1 * n1 + n2 * n2;
        double a = (n4 - n2 * b) / n1;
        double fromLatRadians = atan2(a, b);
        if (fromLatRadians < -PI / 2 || fromLatRadians > PI / 2) {
            b = n2 * n4 - sqrt(discriminant);
            b /= n1 * n1 + n2 * n2;
            fromLatRadians = atan2(a, b);
        }
        if (fromLatRadians < -PI / 2 || fromLatRadians > PI / 2) {
            // No solution which would make sense in LatLng-space.
            return null;
        }
        double fromLngRadians = toRadians(to.longitude) -
                atan2(n3, n1 * cos(fromLatRadians) - n2 * sin(fromLatRadians));
        return new LatLng(toDegrees(fromLatRadians), toDegrees(fromLngRadians));
    }

    /**
     * Returns the LatLng which lies the given fraction of the way between the
     * origin LatLng and the destination LatLng.
     * @param from     The LatLng from which to start.
     * @param to       The LatLng toward which to travel.
     * @param fraction A fraction of the distance to travel.
     * @return The interpolated LatLng.
     */
    public static LatLng interpolate(LatLng from, LatLng to, double fraction) {
        // http://en.wikipedia.org/wiki/Slerp
        double fromLat = toRadians(from.latitude);
        double fromLng = toRadians(from.longitude);
        double toLat = toRadians(to.latitude);
        double toLng = toRadians(to.longitude);
        double cosFromLat = cos(fromLat);
        double cosToLat = cos(toLat);

        // Computes Spherical interpolation coefficients.
        double angle = computeAngleBetween(from, to);
        double sinAngle = sin(angle);
        if (sinAngle < 1E-6) {
            return from;
        }
        double a = sin((1 - fraction) * angle) / sinAngle;
        double b = sin(fraction * angle) / sinAngle;

        // Converts from polar to vector and interpolate.
        double x = a * cosFromLat * cos(fromLng) + b * cosToLat * cos(toLng);
        double y = a * cosFromLat * sin(fromLng) + b * cosToLat * sin(toLng);
        double z = a * sin(fromLat) + b * sin(toLat);

        // Converts interpolated vector back to polar.
        double lat = atan2(z, sqrt(x * x + y * y));
        double lng = atan2(y, x);
        return new LatLng(toDegrees(lat), toDegrees(lng));
    }

    /**
     * Returns distance on the unit sphere; the arguments are in radians.
     */
    private static double distanceRadians(double lat1, double lng1, double lat2, double lng2) {
        return arcHav(havDistance(lat1, lat2, lng1 - lng2));
    }

    /**
     * Returns the angle between two LatLngs, in radians. This is the same as the distance
     * on the unit sphere.
     */
    static double computeAngleBetween(LatLng from, LatLng to) {
        return distanceRadians(toRadians(from.latitude), toRadians(from.longitude),
                               toRadians(to.latitude), toRadians(to.longitude));
    }

    /**
     * Returns the distance between two LatLngs, in meters.
     */
    public static double computeDistanceBetween(LatLng from, LatLng to) {
        return computeAngleBetween(from, to) * EARTH_RADIUS;
    }

    /**
     * Returns the length of the given path, in meters, on Earth.
     */
    public static double computeLength(List<LatLng> path) {
        if (path.size() < 2) {
            return 0;
        }
        double length = 0;
        LatLng prev = path.get(0);
        double prevLat = toRadians(prev.latitude);
        double prevLng = toRadians(prev.longitude);
        for (LatLng point : path) {
            double lat = toRadians(point.latitude);
            double lng = toRadians(point.longitude);
            length += distanceRadians(prevLat, prevLng, lat, lng);
            prevLat = lat;
            prevLng = lng;
        }
        return length * EARTH_RADIUS;
    }

    /**
     * Returns the area of a closed path on Earth.
     * @param path A closed path.
     * @return The path area in square meters.
     */
    public static double computeArea(List<LatLng> path) {
        return abs(computeSignedArea(path));
    }

    /**
     * Returns the signed area of a closed path on Earth. The sign of the area may be used to
     * determine the orientation of the path.
     * "inside" is the surface that does not contain the South Pole.
     * @param path A closed path.
     * @return The loop area in square meters.
     */
    public static double computeSignedArea(List<LatLng> path) {
        return computeSignedArea(path, EARTH_RADIUS);
    }

    /**
     * Returns the signed area of a closed path on a sphere of given radius.
     * The computed area uses the same units as the radius squared.
     * Used by SphericalUtilTest.
     */
    static double computeSignedArea(List<LatLng> path, double radius) {
        int size = path.size();
        if (size < 3) { return 0; }
        double total = 0;
        LatLng prev = path.get(size - 1);
        double prevTanLat = tan((PI / 2 - toRadians(prev.latitude)) / 2);
        double prevLng = toRadians(prev.longitude);
        // For each edge, accumulate the signed area of the triangle formed by the North Pole
        // and that edge ("polar triangle").
        for (LatLng point : path) {
            double tanLat = tan((PI / 2 - toRadians(point.latitude)) / 2);
            double lng = toRadians(point.longitude);
            total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng);
            prevTanLat = tanLat;
            prevLng = lng;
        }
        return total * (radius * radius);
    }

    /**
     * Returns the signed area of a triangle which has North Pole as a vertex.
     * Formula derived from "Area of a spherical triangle given two edges and the included angle"
     * as per "Spherical Trigonometry" by Todhunter, page 71, section 103, point 2.
     * See http://books.google.com/books?id=3uBHAAAAIAAJ&pg=PA71
     * The arguments named "tan" are tan((pi/2 - latitude)/2).
     */
    private static double polarTriangleArea(double tan1, double lng1, double tan2, double lng2) {
        double deltaLng = lng1 - lng2;
        double t = tan1 * tan2;
        return 2 * atan2(t * sin(deltaLng), 1 + t * cos(deltaLng));
    }

    /**
     * Wraps the given value into the inclusive-exclusive interval between min and max.
     * @param n   The value to wrap.
     * @param min The minimum.
     * @param max The maximum.
     */
    static double wrap(double n, double min, double max) {
        return (n >= min && n < max) ? n : (mod(n - min, max - min) + min);
    }

    /**
     * Returns the non-negative remainder of x / m.
     * @param x The operand.
     * @param m The modulus.
     */
    static double mod(double x, double m) {
        return ((x % m) + m) % m;
    }
}

Ответ 4

Карты Google, похоже, тесно работают с милями/пикселями. При масштабировании = 13, 1 миля = 100 пикселей. 2 мили = 200 пикселей. Каждый зум-рендер увеличивается или уменьшается в 2 раза. Поэтому в Zoom 14, 1 миля = 200 пикселей и при увеличении 12, 1 миля = 50 пикселей.

Ответ 5

Я преобразовал принятый ответ, чтобы вернуть двойное значение, поскольку в библиотеке Google Maps Google используются уровни масштабирования с плавающей точкой, а также учитываются широты от экватора.

public static double getZoomForMetersWide (
  final double desiredMeters,
  final double mapWidth,
  final double latitude )
{
  final double latitudinalAdjustment = Math.cos( Math.PI * latitude / 180.0 );

  final double arg = EQUATOR_LENGTH * mapWidth * latitudinalAdjustment / ( desiredMeters * 256.0 );

  return Math.log( arg ) / Math.log( 2.0 );
}

В качестве альтернативы, для наилучших результатов на Android не передается реальный подсчет пикселей, а размер масштабируется для плотности пикселей устройства.

DisplayMetrics metrics = getResources().getDisplayMetrics();
float mapWidth = mapView.getWidth() / metrics.scaledDensity;

Надеюсь, это поможет кому-то.

Ответ 6

Использование цикла для расчета уровня масштабирования очень наивно. Намного лучше использовать математику.

Здесь функция (тип возвращаемого значения: float)

public static double calcZoom(int visible_distance, int img_width)
{
    // visible_distance -> in meters
    // img_width -> in pixels

    visible_distance = Math.abs(visible_distance);
    double equator_length = 40075016; // in meters

    // for an immage of 256 pixel pixel
    double zoom256 = Math.log(equator_length/visible_distance)/Math.log(2);

    // adapt the zoom to the image size
    int x = (int) (Math.log(img_width/256)/Math.log(2));
    double zoom = zoom256 + x;

    return zoom;
}

пример вызова:

public static void main(String[] args)
{
    // computes the zoom for 1km=1000m for an image having 256 width
    double zoom = MainClass.calcZoom(1000, 256);
    System.out.println("zoom: " + String.valueOf(zoom));
    return;
} 

Математические формулы для расчета уровня масштабирования:

equator_length = 40075016
zoom_level = logE(equator_length/distance)/logE(2) + logE(img_width/256)/logE(2)
// The zoom_level computed here is a float number.

Это все люди!: -)

ВНИМАНИЕ: Приведенное выше решение в качестве принятого ответа работает только для уровней масштабирования рядом с экватором. Если вам нужно решение, которое работает со всеми широтами, вам нужна длина параллели на той же широте уровня масштабирования, который вы хотите вычислить. Метод calcZoom меняется на

private double calcZoom(int visible_distance, int img_width, double atLatitude) {
    // visible_distance -> in meters
    // img_width -> in pixels

    double parallel_length = this.calcParallelLegth(atLatitude); // in meters

    // for an immage of 256 pixel pixel
    zoom256 = Math.log(parallel_length/visible_distance))/Math.log(2)

    // adapt the zoom to the image size
    x = (int) Math.log(img_width/256)/Math.log(2)
    zoom = zoom256 + x

    return zoom;
}

Где this.calcParallelLegth(atLatitude) возвращает длину параллели на широте atLatitude.

Вы можете вычислить длину самостоятельно с помощью некоторой библиотеки (предпочтительно, используя формулы Винсенти).

В качестве альтернативы

Если у вас нет такой библиотеки (или вы не ищете библиотеку, или вы просто хотите получить полный работающий код) , в нижней части этого ответа вы можете найти весь рабочий код с реализация double calcParallelLegth(double atLatitude), в которой используется таблица (вычисленная с использованием формул Винсенти) с параллельной длиной во всех широтах с допуском 3%.


Примечание:
ВЫ ДОЛЖНЫ ПРОЧИТАТЬ НИЖЕ НИЖЕ, ЕСЛИ ВЫ И ПОНЯТЬ ФОРМУЛУ (ИЛИ ПРОВЕРИТЬ, ЕСЛИ ФОРМУЛА ПРАВИЛА)

Объяснение формул ниже:

Проще говоря!

Давайте разделим задачу на две части.

Часть 1
рассчитать масштаб для изображения размером 256x256

Часть 2
адаптировать зум для изображения с другим размером

Разрешение Часть 1

Размер изображения 256х256. Уровень масштабирования 0 показывает весь экватор.
каждый последующий уровень масштабирования позволяет мне видеть половину, затем раньше.

Экватор имеет длину 40 075 016 метров (согласно WGS-84 (* 1) и Формулы Винсенти (* 2))

zoom=0 -> 40,075,016 / 1   = 40,075,016 meters visible         Note: 2^0=1
zoom=1 -> 40,075,016 / 2   = 20,037,508 meters visible         Note: 2^1=2
zoom=2 -> 40,075,016 / 4   = 10,018,754 meters visible         Note: 2^2=4
zoom=3 -> 40,075,016 / 8   =  5,009,377 meters visible         Note: 2^3=8
zoom=4 -> 40,075,016 / 16  =  2,504,688.5 meters visible       Note: 2^4=16
zoom=5 -> 40,075,016 / 2^5 =  1,252,344.25 meters visible      Note= 2^5=32
zoom=6 -> 40,075,016 / 2^6 =    636,172.125 meters visible     Note= 2^6=64
... 
zoom   -> equator_length / 2^zoom = visible_distance

Как вы можете видеть выше, каждый последующий уровень масштабирования позволяет мне видеть половину затем.

zoom - это требуемый zoom_level.
visible_distance - это количество метров, которое изображение показывает горизонтально.

если вы хотите 1 км, чем вы должны рассчитать зум с visible_distance = 1000

Позвольте узнать формулы увеличения.
Здесь математика делает магию ("скучные" магические вещи).

   equator_length / 2^zoom = visible_distance ->                            
-> equator_length / visible_distance = 2^zoom ->
-> log2(equator_length / visible_distance) = log2(2^zoom) ->        (*3)
-> log2(equator_length / visible_distance) = zoom*log2(2) ->        (*4)
-> log2(equator_length / visible_distance) = zoom*1 ->              (*5)
-> log2(equator_length / visible_distance) = zoom ->
-> logE(equator_length / visible_distance)/logE(2) = zoom ->          (*6)

Формулы уровня масштабирования для изображения 256x256:

zoom256 = logE(equator_length/visible_distance) / logE(2)

Часть 1 СДЕЛАНО !!

Разрешение Часть 2

Приспособьте увеличение к желаемому размеру изображения.

Каждый раз, когда ширина изображения удваивается, масштаб, необходимый для просмотра всего экватора, увеличивается на единицу.

Пример:
На изображении 512x512 масштаб, необходимый для просмотра всего экватора, равен 1. На изображении 1024x1024 масштаб, необходимый для просмотра всего экватора, равен 2. На изображении 2048x2048 увеличение, необходимое для просмотра всего экватора, составляет 3.

Тем не менее

width= 256 ->  256/256 = 1 ->   zoom=0 (needed to see the whole equator)
width= 512 ->  512/256 = 2   -> zoom=1 (needed to see the whole equator)
width=1024 -> 1024/256 = 4   -> zoom=2 (needed to see the whole equator)
width=2048 -> 2048/256 = 8   -> zoom=3 (needed to see the whole equator)
width=4096 -> 4096/256 = 2^4 -> zoom=4 (needed to see the whole equator)
width=4096 -> 4096/256 = 2^5 -> zoom=5 (needed to see the whole equator)

...  width → width/256 = 2 ^ x → zoom = x (необходимо, чтобы увидеть весь экватор)

это означает, что (zoom_level равен

- with an   512x512    image, the zoom needed is zoom256+1
- with an  1024x1024   image, the zoom needed is zoom256+2
- with an  2048x2048   image, the zoom needed is zoom256+3
...
- with an WIDTHxHEIGHT image, the zoom needed is zoom256+x

Когда требуется x, чтобы адаптировать зум к требуемому размеру изображения.

Итак, это вопрос извлечения х из

width/256 = 2^x

Давай сделаем это

width/256 = 2^x ->
-> log2(width/256) = log2(2^x) ->            (*3)
-> log2(width/256) = x * log2(2) ->          (*4)
-> log2(width/256) = x * 1 ->                (*5)
-> log2(width/256) = x -> 
-> logE(width/256) / logE(2) = x ->          (*6)

Теперь у нас есть формула x.

Формулы уровня масштабирования для изображения WIDTHxHEIGHT:

zoom = zoom256 + x

Итак, если вы хотите, чтобы на изображении 512x512 было видно 1 км, чем

zoom256 = logE(40075016/1000) / logE(2) = 15.29041547592718
x = logE(512/256) / logE(2) = 1
zoom = zoom256 + z = 15.29041547592718 + 1 = 16.29041547592718

Если оно должно быть целым числом

zoom = floor(zoom) = 16

СДЕЛАНО!

(*3) expr1=expr2 <-> log(expr1)=log(expr2)
(*4) logN(a^b) = b * logN(a)
(*5) logN(N) = 1
(*6) logN(expr) = log(expr)/log(N)
(*7) log(a/b) = log(a) - log(b)

Вот полный код, который вычисляет уровень масштабирования на каждой ширине изображения.

class MainClass
{
    public static int getParallelLength(double atLatitude)
    {

        int FR_LAT = 0; // from latitude
        int TO_LAT = 1; // to latidude
        int PA_LEN = 2; // parallel length in meters)
        int PC_ERR = 3; // percentage error

        //  fr_lat| to_lat            |  par_len| perc_err
        double tbl[][] = {
            { 0.00, 12.656250000000000, 40075016, 2.410},
            {12.66, 17.402343750000000, 39107539, 2.180},
            {17.40, 22.148437500000000, 38252117, 2.910},
            {22.15, 25.708007812500000, 37135495, 2.700},
            {25.71, 28.377685546875000, 36130924, 2.330},
            {28.38, 31.047363281250000, 35285940, 2.610},
            {31.05, 33.717041015625000, 34364413, 2.890},
            {33.72, 35.719299316406250, 33368262, 2.380},
            {35.72, 37.721557617187500, 32573423, 2.560},
            {37.72, 39.723815917968750, 31738714, 2.750},
            {39.72, 41.726074218750000, 30865121, 2.950},
            {41.73, 43.227767944335938, 29953681, 2.360},
            {43.23, 44.729461669921875, 29245913, 2.480},
            {44.73, 46.231155395507812, 28517939, 2.620},
            {46.23, 47.732849121093750, 27770248, 2.760},
            {47.73, 49.234542846679688, 27003344, 2.900},
            {49.23, 50.360813140869141, 26217745, 2.290},
            {50.36, 51.487083435058594, 25616595, 2.380},
            {51.49, 52.613353729248047, 25005457, 2.480},
            {52.61, 53.739624023437500, 24384564, 2.580},
            {53.74, 54.865894317626953, 23754152, 2.690},
            {54.87, 55.992164611816406, 23114464, 2.800},
            {55.99, 57.118434906005859, 22465745, 2.920},
            {57.12, 57.963137626647949, 21808245, 2.280},
            {57.96, 58.807840347290039, 21309508, 2.360},
            {58.81, 59.652543067932129, 20806081, 2.440},
            {59.65, 60.497245788574219, 20298074, 2.520},
            {60.50, 61.341948509216309, 19785597, 2.610},
            {61.34, 62.186651229858398, 19268762, 2.700},
            {62.19, 63.031353950500488, 18747680, 2.800},
            {63.03, 63.876056671142578, 18222465, 2.900},
            {63.88, 64.509583711624146, 17693232, 2.250},
            {64.51, 65.143110752105713, 17293739, 2.320},
            {65.14, 65.776637792587280, 16892100, 2.390},
            {65.78, 66.410164833068848, 16488364, 2.460},
            {66.41, 67.043691873550415, 16082582, 2.530},
            {67.04, 67.677218914031982, 15674801, 2.610},
            {67.68, 68.310745954513550, 15265074, 2.690},
            {68.31, 68.944272994995117, 14853450, 2.780},
            {68.94, 69.577800035476685, 14439980, 2.870},
            {69.58, 70.211327075958252, 14024715, 2.970},
            {70.21, 70.686472356319427, 13607707, 2.300},
            {70.69, 71.161617636680603, 13293838, 2.360},
            {71.16, 71.636762917041779, 12979039, 2.430},
            {71.64, 72.111908197402954, 12663331, 2.500},
            {72.11, 72.587053477764130, 12346738, 2.570},
            {72.59, 73.062198758125305, 12029281, 2.640},
            {73.06, 73.537344038486481, 11710981, 2.720},
            {73.54, 74.012489318847656, 11391862, 2.800},
            {74.01, 74.487634599208832, 11071946, 2.890},
            {74.49, 74.962779879570007, 10751254, 2.980},
            {74.96, 75.319138839840889, 10429810, 2.310},
            {75.32, 75.675497800111771, 10188246, 2.370},
            {75.68, 76.031856760382652,  9946280, 2.430},
            {76.03, 76.388215720653534,  9703923, 2.500},
            {76.39, 76.744574680924416,  9461183, 2.560},
            {76.74, 77.100933641195297,  9218071, 2.640},
            {77.10, 77.457292601466179,  8974595, 2.710},
            {77.46, 77.813651561737061,  8730766, 2.790},
            {77.81, 78.170010522007942,  8486593, 2.880},
            {78.17, 78.526369482278824,  8242085, 2.970},
            {78.53, 78.793638702481985,  7997252, 2.290},
            {78.79, 79.060907922685146,  7813420, 2.350},
            {79.06, 79.328177142888308,  7629414, 2.410},
            {79.33, 79.595446363091469,  7445240, 2.470},
            {79.60, 79.862715583294630,  7260900, 2.540},
            {79.86, 80.129984803497791,  7076399, 2.600},
            {80.13, 80.397254023700953,  6891742, 2.680},
            {80.40, 80.664523243904114,  6706931, 2.750},
            {80.66, 80.931792464107275,  6521972, 2.830},
            {80.93, 81.199061684310436,  6336868, 2.920},
            {81.20, 81.399513599462807,  6151624, 2.250},
            {81.40, 81.599965514615178,  6012600, 2.310},
            {81.60, 81.800417429767549,  5873502, 2.360},
            {81.80, 82.000869344919920,  5734331, 2.420},
            {82.00, 82.201321260072291,  5595088, 2.480},
            {82.20, 82.401773175224662,  5455775, 2.550},
            {82.40, 82.602225090377033,  5316394, 2.620},
            {82.60, 82.802677005529404,  5176947, 2.690},
            {82.80, 83.003128920681775,  5037435, 2.770},
            {83.00, 83.203580835834146,  4897860, 2.850},
            {83.20, 83.404032750986516,  4758224, 2.930},
            {83.40, 83.554371687350795,  4618528, 2.260},
            {83.55, 83.704710623715073,  4513719, 2.320},
            {83.70, 83.855049560079351,  4408878, 2.370},
            {83.86, 84.005388496443629,  4304006, 2.430},
            {84.01, 84.155727432807907,  4199104, 2.490},
            {84.16, 84.306066369172186,  4094172, 2.560},
            {84.31, 84.456405305536464,  3989211, 2.630},
            {84.46, 84.606744241900742,  3884223, 2.700},
            {84.61, 84.757083178265020,  3779207, 2.770},
            {84.76, 84.907422114629298,  3674165, 2.850},
            {84.91, 85.057761050993577,  3569096, 2.940},
            {85.06, 85.170515253266785,  3464003, 2.270},
            {85.17, 85.283269455539994,  3385167, 2.320},
            {85.28, 85.396023657813203,  3306318, 2.380},
            {85.40, 85.508777860086411,  3227456, 2.440},
            {85.51, 85.621532062359620,  3148581, 2.500},
            {85.62, 85.734286264632829,  3069693, 2.570},
            {85.73, 85.847040466906037,  2990793, 2.630},
            {85.85, 85.959794669179246,  2911882, 2.710},
            {85.96, 86.072548871452454,  2832959, 2.780},
            {86.07, 86.185303073725663,  2754025, 2.860},
            {86.19, 86.298057275998872,  2675080, 2.950},
            {86.30, 86.382622927703778,  2596124, 2.280},
            {86.38, 86.467188579408685,  2536901, 2.330},
            {86.47, 86.551754231113591,  2477672, 2.390},
            {86.55, 86.636319882818498,  2418437, 2.440},
            {86.64, 86.720885534523404,  2359197, 2.510},
            {86.72, 86.805451186228311,  2299952, 2.570},
            {86.81, 86.890016837933217,  2240701, 2.640},
            {86.89, 86.974582489638124,  2181446, 2.710},
            {86.97, 87.059148141343030,  2122186, 2.790},
            {87.06, 87.143713793047937,  2062921, 2.870},
            {87.14, 87.228279444752843,  2003652, 2.950},
            {87.23, 87.291703683531523,  1944378, 2.280},
            {87.29, 87.355127922310203,  1899919, 2.340},
            {87.36, 87.418552161088883,  1855459, 2.390},
            {87.42, 87.481976399867563,  1810996, 2.450},
            {87.48, 87.545400638646242,  1766531, 2.510},
            {87.55, 87.608824877424922,  1722063, 2.580},
            {87.61, 87.672249116203602,  1677594, 2.650},
            {87.67, 87.735673354982282,  1633122, 2.720},
            {87.74, 87.799097593760962,  1588648, 2.790},
            {87.80, 87.862521832539642,  1544172, 2.880},
            {87.86, 87.925946071318322,  1499695, 2.960},
            {87.93, 87.973514250402332,  1455215, 2.290},
            {87.97, 88.021082429486341,  1421854, 2.340},
            {88.02, 88.068650608570351,  1388493, 2.400},
            {88.07, 88.116218787654361,  1355130, 2.460},
            {88.12, 88.163786966738371,  1321766, 2.520},
            {88.16, 88.211355145822381,  1288401, 2.580},
            {88.21, 88.258923324906391,  1255036, 2.650},
            {88.26, 88.306491503990401,  1221669, 2.730},
            {88.31, 88.354059683074411,  1188302, 2.800},
            {88.35, 88.401627862158421,  1154934, 2.880},
            {88.40, 88.449196041242431,  1121565, 2.970},
            {88.45, 88.484872175555438,  1088195, 2.290},
            {88.48, 88.520548309868445,  1063167, 2.350},
            {88.52, 88.556224444181453,  1038139, 2.410},
            {88.56, 88.591900578494460,  1013110, 2.470},
            {88.59, 88.627576712807468,   988081, 2.530},
            {88.63, 88.663252847120475,   963052, 2.590},
            {88.66, 88.698928981433482,   938022, 2.660},
            {88.70, 88.734605115746490,   912992, 2.740},
            {88.73, 88.770281250059497,   887961, 2.810},
            {88.77, 88.805957384372505,   862930, 2.900},
            {88.81, 88.841633518685512,   837899, 2.980},
            {88.84, 88.868390619420268,   812867, 2.300},
            {88.87, 88.895147720155023,   794093, 2.360},
            {88.90, 88.921904820889779,   775319, 2.420},
            {88.92, 88.948661921624534,   756545, 2.480},
            {88.95, 88.975419022359290,   737771, 2.540},
            {88.98, 89.002176123094046,   718996, 2.610},
            {89.00, 89.028933223828801,   700221, 2.680},
            {89.03, 89.055690324563557,   681446, 2.750},
            {89.06, 89.082447425298312,   662671, 2.830},
            {89.08, 89.109204526033068,   643896, 2.910},
            {89.11, 89.129272351584135,   625121, 2.250},
            {89.13, 89.149340177135201,   611039, 2.300},
            {89.15, 89.169408002686268,   596957, 2.350},
            {89.17, 89.189475828237335,   582876, 2.410},
            {89.19, 89.209543653788401,   568794, 2.470},
            {89.21, 89.229611479339468,   554712, 2.530},
            {89.23, 89.249679304890535,   540630, 2.600},
            {89.25, 89.269747130441601,   526548, 2.670},
            {89.27, 89.289814955992668,   512466, 2.740},
            {89.29, 89.309882781543735,   498384, 2.820},
            {89.31, 89.329950607094801,   484302, 2.900},
            {89.33, 89.350018432645868,   470219, 2.990},
            {89.35, 89.365069301809172,   456137, 2.310},
            {89.37, 89.380120170972475,   445575, 2.370},
            {89.38, 89.395171040135779,   435013, 2.420},
            {89.40, 89.410221909299082,   424451, 2.480},
            {89.41, 89.425272778462386,   413889, 2.550},
            {89.43, 89.440323647625689,   403328, 2.610},
            {89.44, 89.455374516788993,   392766, 2.680},
            {89.46, 89.470425385952296,   382204, 2.760},
            {89.47, 89.485476255115600,   371642, 2.840},
            {89.49, 89.500527124278904,   361080, 2.920},
            {89.50, 89.511815276151381,   350518, 2.260},
            {89.51, 89.523103428023859,   342596, 2.310},
            {89.52, 89.534391579896337,   334674, 2.360},
            {89.53, 89.545679731768814,   326753, 2.420},
            {89.55, 89.556967883641292,   318831, 2.480},
            {89.56, 89.568256035513770,   310910, 2.540},
            {89.57, 89.579544187386247,   302988, 2.610},
            {89.58, 89.590832339258725,   295066, 2.680},
            {89.59, 89.602120491131203,   287145, 2.750},
            {89.60, 89.613408643003680,   279223, 2.830},
            {89.61, 89.624696794876158,   271301, 2.910},
            {89.62, 89.633162908780520,   263380, 2.250},
            {89.63, 89.641629022684882,   257438, 2.300},
            {89.64, 89.650095136589243,   251497, 2.360},
            {89.65, 89.658561250493605,   245556, 2.410},
            {89.66, 89.667027364397967,   239615, 2.470},
            {89.67, 89.675493478302329,   233673, 2.540},
            {89.68, 89.683959592206691,   227732, 2.600},
            {89.68, 89.692425706111052,   221791, 2.670},
            {89.69, 89.700891820015414,   215849, 2.750},
            {89.70, 89.709357933919776,   209908, 2.830},
            {89.71, 89.717824047824138,   203967, 2.910},
            {89.72, 89.724173633252406,   198026, 2.250},
            {89.72, 89.730523218680673,   193570, 2.300},
            {89.73, 89.736872804108941,   189114, 2.350},
            {89.74, 89.743222389537209,   184658, 2.410},
            {89.74, 89.749571974965477,   180202, 2.470},
            {89.75, 89.755921560393745,   175746, 2.530},
            {89.76, 89.762271145822012,   171290, 2.600},
            {89.76, 89.768620731250280,   166834, 2.670},
            {89.77, 89.774970316678548,   162378, 2.740},
            {89.77, 89.781319902106816,   157922, 2.820},
            {89.78, 89.787669487535084,   153466, 2.900},
            {89.79, 89.794019072963351,   149010, 2.990},
            {89.79, 89.798781262034552,   144554, 2.310},
            {89.80, 89.803543451105753,   141212, 2.360},
            {89.80, 89.808305640176954,   137869, 2.420},
            {89.81, 89.813067829248155,   134527, 2.480},
            {89.81, 89.817830018319356,   131185, 2.540},
            {89.82, 89.822592207390556,   127843, 2.610},
            {89.82, 89.827354396461757,   124501, 2.680},
            {89.83, 89.832116585532958,   121159, 2.750},
            {89.83, 89.836878774604159,   117817, 2.830},
            {89.84, 89.841640963675360,   114475, 2.910},
            {89.84, 89.845212605478764,   111133, 2.250},
            {89.85, 89.848784247282168,   108627, 2.300},
            {89.85, 89.852355889085572,   106120, 2.360},
            {89.85, 89.855927530888977,   103614, 2.410},
            {89.86, 89.859499172692381,   101107, 2.470},
            {89.86, 89.863070814495785,    98601, 2.540},
            {89.86, 89.866642456299189,    96094, 2.600},
            {89.87, 89.870214098102593,    93588, 2.670},
            {89.87, 89.873785739905998,    91081, 2.750},
            {89.87, 89.877357381709402,    88575, 2.830},
            {89.88, 89.880929023512806,    86068, 2.910},
            {89.88, 89.883607754865352,    83562, 2.240},
            {89.88, 89.886286486217898,    81682, 2.300},
            {89.89, 89.888965217570444,    79802, 2.350},
            {89.89, 89.891643948922990,    77922, 2.410},
            {89.89, 89.894322680275536,    76042, 2.470},
            {89.89, 89.897001411628082,    74162, 2.530},
            {89.90, 89.899680142980628,    72282, 2.600},
            {89.90, 89.902358874333174,    70402, 2.660},
            {89.90, 89.905037605685720,    68523, 2.740},
            {89.91, 89.907716337038266,    66643, 2.820},
            {89.91, 89.910395068390812,    64763, 2.900},
            {89.91, 89.913073799743358,    62883, 2.980},
            {89.91, 89.915082848257768,    61003, 2.310},
            {89.92, 89.917091896772178,    59593, 2.360},
            {89.92, 89.919100945286587,    58183, 2.420},
            {89.92, 89.921109993800997,    56773, 2.480},
            {89.92, 89.923119042315406,    55363, 2.540},
            {89.92, 89.925128090829816,    53953, 2.610},
            {89.93, 89.927137139344225,    52543, 2.680},
            {89.93, 89.929146187858635,    51134, 2.750},
            {89.93, 89.931155236373044,    49724, 2.830},
            {89.93, 89.933164284887454,    48314, 2.910},
            {89.93, 89.934671071273257,    46904, 2.250},
            {89.93, 89.936177857659061,    45846, 2.300},
            {89.94, 89.937684644044865,    44789, 2.360},
            {89.94, 89.939191430430668,    43731, 2.410},
            {89.94, 89.940698216816472,    42674, 2.470},
            {89.94, 89.942205003202275,    41617, 2.540},
            {89.94, 89.943711789588079,    40559, 2.600},
            {89.94, 89.945218575973882,    39502, 2.670},
            {89.95, 89.946725362359686,    38444, 2.740},
            {89.95, 89.948232148745490,    37387, 2.820},
            {89.95, 89.949738935131293,    36329, 2.900}
        };

        for(int r=0; r < tbl.length; r++)
        {
            double fromLat = tbl[r][FR_LAT];
            double toLat = tbl[r][TO_LAT];
            double atLat = atLatitude;

            if(fromLat <= atLat && atLat < toLat)
            {
                double parallelLength = tbl[r][PA_LEN];
                return (int)parallelLength;
            } 
        }

        return 0;
    }

    public static double calcZoom(int visible_distance, int img_width, double atLat)
    {
        // visible_distance -> in meters
        // img_width -> in pixels
        // atLat -> the latitude you want the zoom level

        visible_distance = Math.abs(visible_distance);
        double parallel_length = MainClass.getParallelLength(atLat); // in meters

        // for an immage of 256 pixel pixel
        double zoom256 = Math.log(parallel_length/visible_distance)/Math.log(2);

        // adapt the zoom to the image size
        int x = (int) (Math.log(img_width/256)/Math.log(2));
        double zoom = zoom256 + x;

        return zoom;
    }

    public static void main(String[] args)
    {
        int len;
        double zoom;

        // equator length
        len = MainClass.getParallelLength(0);
        System.out.println("parallel length at 0: " + String.valueOf(len));

        // legth parallel at latitude 89.9 (near the north pole)
        len = MainClass.getParallelLength(89.9);
        System.out.println("parallel length at 89.9: " + String.valueOf(len));

        // the zoom level needed to see 100km=100000m in a img having 
        // width 256 at equator latitude
        zoom = MainClass.calcZoom(100000, 256, 0);
        System.out.println("zoom (100km, width:256, lat:0): " + String.valueOf(zoom));

        // the zoom level needed to see 100km=100000m in a img having 
        // width 512 at equator latitude
        zoom = MainClass.calcZoom(100000, 512, 0);
        System.out.println("zoom (100km, width:512, lat:0): " + String.valueOf(zoom));

        // the zoom level needed to see 100km=100000m in a img having 
        // width 256 at latitude 60
        zoom = MainClass.calcZoom(100000, 256, 60);
        System.out.println("zoom (100km, width:256, lat:60): " + String.valueOf(zoom));

        return;
    }
}

Ответ 7

ЗАКЛЮЧИТЕЛЬНОЕ рабочее решение:

public static void getZoomForMetersWide(GoogleMap googleMap, int mapViewWidth, LatLng latLngPoint, int desiredMeters) {
        DisplayMetrics metrics = App.getAppCtx().getResources().getDisplayMetrics();
        float mapWidth = mapViewWidth / metrics.density;

        final int EQUATOR_LENGTH = 40075004;
        final int TIME_ANIMATION_MILIS = 1500;
        final double latitudinalAdjustment = Math.cos(Math.PI * latLngPoint.latitude / 180.0);
        final double arg = EQUATOR_LENGTH * mapWidth * latitudinalAdjustment / (desiredMeters * 256.0);
        double valToZoom = Math.log(arg) / Math.log(2.0);

        googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLngPoint, Float.valueOf(String.valueOf(valToZoom))), TIME_ANIMATION_MILIS , null);
    }

P.S. Используя @sho answer и @Lionel Briand comment

Ответ 8

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

 mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
        private float currentZoom = -1;
        @Override
        public void onCameraChange(CameraPosition position) {
            if (position.zoom != currentZoom){
                currentZoom = position.zoom;  // here you get zoom level
                Toast.makeText(this, "Zoom Value is : "+currentZoom, Toast.LENGTH_SHORT).show();
            }
        }
    });