计算给定范围和地图大小,fitBounds()返回的视口

菲利普·尼尔森(Filip Nilsson)

我有一个带有服务器端和客户端组件的Web应用程序。我在两侧使用Google Maps HTTP API和Google Maps Javascript API执行相同的地理编码请求。

对于给定的查询,两个API都使用相同的视口进行响应,这一切都很好。

我的问题是我map.fitBounds()在客户端的给定视口中使用该方法来缩放和平移地图以包含视口。问题是我想在服务器端执行相同的操作,以允许我尽快向客户端提供适当的数据。

当前方法是在客户端渲染地图,然后将渲染地图实际视口发送到服务器以执行计算。我的目标是删除此步骤,并在客户端甚至开始渲染地图之前执行计算。

这个JSBin,在Bounds 1和中显示我在说什么Bounds 3fitBounds()被调用时,地图缩放,以尽可能高的水平仍然包含了所有的矩形(根据我的研究,围绕绑定额外的45像素保证金)。

我正在寻找这个实际视口的地理坐标

在这个SO答案中John-S提供了一种方法来计算给定维度中地图的最高可能缩放级别。

我认为应该可以对其进行修改以适合我的需求,但是我不确定如何完成我的墨卡托投影技巧。

函数应采用边界(swne坐标对)和地图div的大小,并返回将返回的地理边界map.fitBounds

菲利普·尼尔森(Filip Nilsson)

我最终基于链接的答案找到了此代码,该代码提供了计算缩放级别和适当的视口的功能。

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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

计算给定东北和西南的视口坐标

来自分类Dev

Libgdx:使用视口时如何计算子画面大小

来自分类Dev

使用JavaScript计算li元素和视口之间的距离

来自分类Dev

Bootstrap 断点视口大小

来自分类Dev

字体和视口libgdx

来自分类Dev

srcset和视口宽度

来自分类Dev

字体和视口libgdx

来自分类Dev

谷歌地图视口自动调整

来自分类Dev

谷歌地图视口自动调整

来自分类Dev

是否可以获取计算的vue变量以根据视口大小进行更改?

来自分类Dev

全屏元素调整为视口大小

来自分类Dev

使用webdriverJS更改phantomJS的视口大小

来自分类Dev

移动键盘调整视口大小

来自分类Dev

在显示之前获取QScrollArea视口的大小

来自分类Dev

根据视口调整画布的大小

来自分类Dev

使用webdriverJS更改phantomJS的视口大小

来自分类Dev

使用jQuery确定视口大小

来自分类Dev

缩放至模型以适合视口大小

来自分类Dev

如何为跨浏览器支持调整视口大小和缩放比例?

来自分类Dev

iPhone6和iPhone 6 Plus的设备宽度CSS视口大小是多少

来自分类Dev

响应式设计中的字体大小和元视口

来自分类Dev

如何解决视口错误的非有限位置和/或大小?

来自分类Dev

MySQL从给定ID和返回计数值的范围之间计算价格的不同值的行

来自分类Dev

使用角度Google地图获取视口的地图位置

来自分类Dev

iPhone和iPad纵向的视口

来自分类Dev

libgdx->相机和视口

来自分类Dev

调整<body>的大小以创建视口大小的背景图像

来自分类Dev

如何创建适合Google地图视口的圆?

来自分类Dev

移动设备:通过CSS获取屏幕区域的大小和位置,而不仅仅是视口的