我有一个带有服务器端和客户端组件的Web应用程序。我在两侧使用Google Maps HTTP API和Google Maps Javascript API执行相同的地理编码请求。
对于给定的查询,两个API都使用相同的视口进行响应,这一切都很好。
我的问题是我map.fitBounds()
在客户端的给定视口中使用该方法来缩放和平移地图以包含视口。问题是我想在服务器端执行相同的操作,以允许我尽快向客户端提供适当的数据。
当前方法是在客户端渲染地图,然后将渲染地图的实际视口发送到服务器以执行计算。我的目标是删除此步骤,并在客户端甚至开始渲染地图之前执行计算。
这个JSBin,在Bounds 1
和中显示我在说什么Bounds 3
。当fitBounds()
被调用时,地图缩放,以尽可能高的水平仍然包含了所有的矩形(根据我的研究,围绕绑定额外的45像素保证金)。
我正在寻找这个实际视口的地理坐标。
在这个SO答案中,John-S提供了一种方法来计算给定维度中地图的最高可能缩放级别。
我认为应该可以对其进行修改以适合我的需求,但是我不确定如何完成我的墨卡托投影技巧。
函数应采用边界(sw
,ne
坐标对)和地图div的大小,并返回将返回的地理边界map.fitBounds
。
我最终基于链接的答案找到了此代码,该代码提供了计算缩放级别和适当的视口的功能。
from math import radians, cos, sin, atan2, sqrt, pow, pi, log, floor, tan, atan, exp
class Coordinate(object):
def __init__(self, lat, lng):
"""
Represents a coordinate with decimal latitude and longitude in degrees
:type lat: float
:type lng: float
"""
self.lat = lat
self.lng = lng
def in_rectangle(self, box):
"""
:param box: Box
:return: True or False
"""
return (box.south_west.lat <= self.lat <= box.north_east.lat) and \
(box.south_west.lng <= self.lng <= box.north_east.lng)
def distance_to_coordinate(self, another_coordinate):
# convert decimal degrees to radians
"""
Calculates the distance between the coordinate and the supplied coordinate. Based on the Haversine formula.
Returns the value in meters.
:param another_coordinate: Coordinate
:return: float
"""
lon1, lat1, lon2, lat2 = map(radians, [self.lng, self.lat, another_coordinate.lng, another_coordinate.lat])
# haversine formula
delta_lon = lon2 - lon1
delta_lat = lat2 - lat1
a = sin(delta_lat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(delta_lon / 2) ** 2
c = 2 * atan2(sqrt(a), sqrt(1-a))
km = 6378100 * c
return km
class Box(object):
def __init__(self, south_west, north_east):
"""
Represents a rectangle on the sphere with the south west and north east corners.
:type north_east: Coordinate
:type south_west: Coordinate
"""
self.north_east = north_east
self.south_west = south_west
def center(self):
"""
Calculates the center point of the box
:rtype : Coordinate
"""
lat_center = self.south_west.lat + (self.north_east.lat-self.south_west.lat)/2
lng_center = self.south_west.lng + (self.north_east.lng-self.south_west.lng)/2
return Coordinate(lat_center, lng_center)
def max_zoom_level_to_fit_box_in_element(box, element_width, element_height):
"""
Calculates the maximum zoom level that would fit box in a map element with the specified width and height
:type box: Box
:type element_height: float
:type element_width: float
"""
WORLD_HEIGHT = 256
WORLD_WIDTH = 256
ZOOM_MAX = 21
ne = box.north_east
sw = box.south_west
lat_fraction = (_lat_to_rad(ne.lat) - _lat_to_rad(sw.lat)) / pi
lng_delta = ne.lng - sw.lng
lng_fraction = ((lng_delta+360) if lng_delta < 0 else lng_delta)/360
lat_zoom = _zoom(element_height, WORLD_HEIGHT, lat_fraction)
lng_zoom = _zoom(element_width, WORLD_WIDTH, lng_fraction)
return min(lat_zoom, lng_zoom, ZOOM_MAX)
def viewport_for_box_in_element(box, element_width, element_height, zoom):
"""
Calculates the viewport, rectangle, which will fit box within it for a given map size and zoom level.
The return value is a box, with coordinates on the sphere.
:param box: Box
:param element_width: float
:param element_height: float
:param zoom: float
:return: Box
"""
center_x, center_y = _lat_and_lng_to_x_and_y_for_zoom(box.center().lat, box.center().lng, zoom)
viewport_sw_x, viewport_sw_y = center_x - element_width/2, center_y+element_height/2
viewport_ne_x, viewport_ne_y = center_x + element_width/2, center_y-element_height/2
viewport_sw = _x_and_y_to_lat_and_lng_for_zoom(viewport_sw_x, viewport_sw_y, zoom)
viewport_ne = _x_and_y_to_lat_and_lng_for_zoom(viewport_ne_x, viewport_ne_y, zoom)
return Box(viewport_sw, viewport_ne)
def _lat_and_lng_to_x_and_y_for_zoom(lat, lng, zoom):
"""
Converts decimal degree coordinates on the sphere to x and y points on the flat Web Mercator projection, for a
given zoom level.
:param lat: float
:param lng: float
:param zoom: float
:return: x,y: float
"""
lat_rad = lat * (pi/180)
lng_rad = lng * (pi/180)
x = (128/pi)*pow(2, zoom)*(lng_rad + pi)
y = (128/pi)*pow(2, zoom)*(pi-log(tan(pi/4+lat_rad/2)))
return x, y
def _x_and_y_to_lat_and_lng_for_zoom(x, y, zoom):
"""
Converts x and y points on the flat Web Mercator projection, for a given zoom level, to decimal degrees on the
spehere.
:param x: float
:param y: float
:param zoom: float
:return: Coordinate
"""
lng_rad = (x*pi)/(pow(2, zoom)*128)-pi
lat_rad = 2*atan(exp(pi-((y*pi)/(pow(2, zoom)*128))))-pi/2
return Coordinate(lat_rad*(180/pi), lng_rad*(180/pi))
def _lat_to_rad(lat):
"""
Converts a decimal degree latitude to radians, given the fact how Web Mercator projection wraps on the latitude.
:param lat: float
:return: float
"""
_sin = sin(lat * pi / 180)
rad_x2 = log((1 + _sin) / (1 - _sin)) / 2
return max(min(rad_x2, pi), -pi) / 2
def _zoom(map_px, world_px, fraction):
"""
Calculates a zoom level
:param map_px: float
:param world_px: float
:param fraction: float
:return:
"""
ln_2 = log(2)
return floor(log(map_px / world_px / fraction) / ln_2)
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句