Android:如何将地图视图的缩放级别设置为当前位置周围 1 公里半径?
Posted
技术标签:
【中文标题】Android:如何将地图视图的缩放级别设置为当前位置周围 1 公里半径?【英文标题】:Android: How do I set the zoom level of map view to 1 km radius around my current location? 【发布时间】:2011-08-25 13:04:39 【问题描述】:我想将地图视图设置为半径为 1 公里,但不知道如何设置?
文档说缩放级别 1 会将地球赤道映射到 256 像素。那么如何计算需要设置的缩放级别,以便地图视图以 1KM 为半径显示区域?
更新: 在阅读了几篇博文后,我编写了以下代码:
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 中计算 每像素米,根据谷歌的说法,它使用 256 像素显示地球的赤道。现在每个后续缩放级别都会放大 2 级,因此对于每个缩放级别,我将每个像素的米减半。我这样做直到我有一个缩放级别,其中 每像素米 乘以 屏幕宽度 得到小于 2000 即 2 公里宽。
但我不认为我得到的缩放级别是显示 2 公里半径的地图。有人可以告诉我我在这里做错了什么吗?
【问题讨论】:
你的想法没问题,但问题是你设置的equatorLength,正确的值在40075004米左右。 (***) 【参考方案1】:虽然这个答案是合乎逻辑的,我发现它有效,但结果不准确,我不知道为什么,但我厌倦了这种方法,而且这种技术更准确。
1) 在具有所需半径的物体上画一个圆
Circle circle = mGoogleMap.addCircle(new CircleOptions().center(new LatLng(latitude, longitude)).radius(getRadiusInMeters()).strokeColor(Color.RED));
circle.setVisible(true);
getZoomLevel(circle);
2) 将该对象传递给此函数并设置缩放级别 这里是a link
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;
【讨论】:
正是我所需要的。我发现 ios 库更有效地处理需要在地图上显示的内容 不错的计算,我自己进行了更改,以便它准确...` public static float zoomLevel(Circle circle) float zoomLevel = 15; if (circle != null) 双半径 = circle.getRadius();双刻度=半径/500; zoomLevel =(float) (16 - Math.log(scale) / Math.log(2)); Log.i(TAG, "缩放级别 = " + zoomLevel ); 返回 zoomLevel - 0.5f ; ` 超出了 0.5f,float 是返回值,因为缩放级别也是浮动的。 500 的基础是什么? 但实际上半径是已知的。那么,我们真的需要一个圈子吗?【参考方案2】:以下代码是最终使用的。给定屏幕宽度以及在缩放级别 1 时地球的赤道长度为 256 像素这一事实,并且每个后续缩放级别都会使表示地球赤道所需的像素数翻倍,以下函数返回屏幕将显示区域的缩放级别2公里宽。
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;
【讨论】:
developers.google.com/maps/documentation/javascript/… 这可能表明缩放级别 1 的地图有 256x256 像素。是这个原因吗? @Inn_vita:您可以简单地将值 2000 更改为 20000 即,而不是使用 "while ((metersPerPixel * widthInPixels) > 2000)" 使用 "while ((metersPerPixel * widthInPixels) > 20000)跨度> 好吧,不可能精确到 1 公里。在每次迭代中,可见的减少到一半。因此,只要它大于 2000,它的一半就会大于 1000,但如果它低于 2000,那么一半就会小于 1000,这是不可取的。所以我使用等于或大于 1 公里半径的最小缩放值。 嗯,我想这可行,但是缩放级别现在可以是浮动的,所以你真正想要的是缩放到跨度,半径为中心点的 ± lat lng 我还想指出,根据参考文档,它是 256dp 而不是 256px。如上所述,这将在不同密度的设备上产生不同的结果。当然,除非传入正确的 dp 数量而不是 px。【参考方案3】:我最终使用了以下工具:
https://github.com/googlemaps/android-maps-utils
我从库中提取了类,因此您不需要整个库。 您无需设置缩放级别,而是使用边界。结果是一样的。
精确显示 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();
从库中提取(略有更改)的类:
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's 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's 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;
【讨论】:
嗨,mapSideInPixels,你能告诉我在哪里可以找到它吗? 我更改了我的代码更新了我的代码:int mapSideInPixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mapHeightInDP, r.getDisplayMetrics()); 你能告诉我你对MARKER_BOUNDS的定义吗?非常感谢! 它只是标记和地图之间的边界之间的空间。这真的无所谓。考虑投票给我的答案,让其他人注意到答案! 嗨@Oritm,我可以从这个答案中获得一些信息以满足我的需要。我想为“X”缩放级别的半径计算设备上的像素数。基本上我在地图上覆盖了自己的圆圈来创建区域。现在我想根据缩放级别调整圆圈的宽度。你能帮我一个线索吗?【参考方案4】:Google 地图似乎与英里/像素密切相关。在缩放 = 13 时,1 英里 = 100 像素。 2 英里 = 200 像素。每个缩放 leven 增加或减少 2 倍。因此,在缩放 14 时,1 英里 = 200 像素,在缩放 12 时,1 英里 = 50 像素。
【讨论】:
嗨@Thomas,我想计算“X”缩放级别半径的设备上的像素数。基本上我在地图上覆盖了自己的圆圈来创建区域。现在我想根据缩放级别调整圆圈的宽度。你能帮我一个线索吗?【参考方案5】:我已将接受的答案转换为返回双精度值,因为 Android 谷歌地图库使用浮点缩放级别,并且还考虑了远离赤道的纬度。
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;
希望这对某人有所帮助。
【讨论】:
什么是EQUATOR_LENGTH
?
赤道长度,米,约6378140
mapWidth
是否需要以像素或 dp 为单位。
应该使用metrics.density
,而不是metrics.scaledDensity
。 scaledDensity
用于缩放文本。
与EQUATOR_LENGTH = 40075004
和metrics.density
完美合作,谢谢@sho 你救了我的一天【参考方案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
纬度处的平行线长度。
您可以使用一些库自己计算长度(最好使用 Vincenty 公式)。
或者
如果您没有这样的库(或者您没有搜索库,或者您只是想要一个完整的代码)在这个答案的底部,您可以找到整个工作代码 使用double calcParallelLegth(double atLatitude)
的实现,它使用一个表(使用文森特公式计算),在所有纬度具有平行长度,容差为 3%。
注意:只有在您理解公式时才需要阅读以下内容(或检查公式是否正确)
公式说明如下:
简单地说!
让我们把问题分成两部分。
第 1 部分 计算 256x256 尺寸图像的缩放比例
第 2 部分 调整缩放以适应不同尺寸的图像
解决第 1 部分 图像尺寸为 256x256。 缩放级别 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 是所需的缩放级别。visible_distance 是图像水平显示的米数。
如果您想要 1 公里,则必须使用 visible_distance=1000
计算 zoom让我们找出缩放公式。 这就是数学的神奇之处(“无聊”的神奇东西)。
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 来调整缩放到想要的图像大小。
所以,这是从
中提取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);
附言使用@sho 回答和@Lionel Briand 评论
【讨论】:
这是最好的公式 公式中的 256 是什么?有没有这个的文档【参考方案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();
);
【讨论】:
以上是关于Android:如何将地图视图的缩放级别设置为当前位置周围 1 公里半径?的主要内容,如果未能解决你的问题,请参考以下文章
Google Maps API v3:如何将缩放级别和地图中心设置为用户提交的位置?