yandex map 3.0 Zoom при клике на кластер

Рейтинг: 0Ответов: 1Опубликовано: 24.05.2023

При клике на кластер необходимо приблизить карту до размеров что бы поместились все маркеры кластера в видимой области.

Для вычисления Bounds использую функцию

_getBounds() {
        var x1 = Infinity,
            x2 = 0,
            y1 = Infinity,
            y2 = 0;

        for (var i = 0; i < this._props.length; i++) {
            var prop = this._props[i];
            x1 = Math.min(prop.geometry.coordinates[0], x1);
            x2 = Math.max(prop.geometry.coordinates[0], x2);
            y1 = Math.min(prop.geometry.coordinates[1], y1);
            y2 = Math.max(prop.geometry.coordinates[1], y2);
        }
        return [[x1, y1], [x2, y2]];
    }

//Получение центра
_getCenter(){
        var bounds = _getBounds();
        var x = bounds[0][0] + (bounds[1][0] - bounds[0][0]) / 2;
        var y = bounds[0][1] + (bounds[1][1] - bounds[0][1]) / 2;
        return [x, y];
    }

Для приближения карты использовал map.setLocation({bounds: _getBounds(), duration: 1000}); однако в этом случае некоторые маркеры находятся на границе видимой области, так же при использовании такой конструкции меняется видимая область видимости маркеров (они пропадают с карты не доходя до границы видимой области карты).

Подозреваю что нужно использовать метод

map.update({
    location: {
        center: _getCenter(),
        zoom: ???,
        duration: 1000,
    }
})

но в этом случае как то нужно вычислить zoom, как это сделать? Возможно есть какие то методы?

Ответы

▲ 1Принят

Сам решил проблему посмотрев исходники предыдущей версии API

         /**
         * Получение масштаба
         * @param {[Array, Array]} bounds
         * @param {YMap} map
         * @param {Boolean} inscribe вписывать область в карту
         * @param {Boolean} floor округляем результат в меньшую сторону
         * @returns {Number}
         */
        function getScale(bounds, map, inscribe, floor) {
            if (typeof inscribe === "undefined") {
                inscribe = true;
            }
            if (typeof floor === "undefined") {
                floor = false;
            }
            var pixelBounds = toGlobalPixelBounds(bounds, 0);
            // 1e-10 чтобы не было деления на 0
            var deltaX = Math.max(Math.abs(pixelBounds[1][0] - pixelBounds[0][0]), 1e-10);
            var deltaY = Math.max(Math.abs(pixelBounds[1][1] - pixelBounds[0][1]), 1e-10);
            var logX = Math.log(map.size.x / deltaX) * Math.LOG2E;
            var logY = Math.log(map.size.y / deltaY) * Math.LOG2E;
            var result = Math.min(Math.max(0, inscribe ? Math.min(logX, logY) : Math.max(logX, logY)), map.zoomRange.max);
            return floor ? Math.floor(result + 1e-10) : result;
        }
function toGlobalPixelBounds(geoBounds, zoom) {
        if (typeof zoom === "undefined") {
            zoom = 0;
        }
    
        var lowerCorner = toGlobalPixels(geoBounds[0], zoom);
        var upperCorner = toGlobalPixels(geoBounds[1], zoom);
        var projectionCycled = [true, false];
        var worldSize = calculateWorldSize(zoom);
        var result = [lowerCorner.slice(), upperCorner.slice()];
        if (lowerCorner[0] > upperCorner[0]) {
            if (projectionCycled[0]) {
                result[0][0] = lowerCorner[0];
                result[1][0] = upperCorner[0] + worldSize;
            } else {
                result[0][0] = upperCorner[0];
                result[1][0] = lowerCorner[0];
            }
        }
        if (lowerCorner[1] > upperCorner[1]) {
            if (projectionCycled[1]) {
                result[0][1] = lowerCorner[1];
                result[1][1] = upperCorner[1] + worldSize;
            } else {
                result[0][1] = upperCorner[1];
                result[1][1] = lowerCorner[1];
            }
        }
        return result;
    }
    
    function toGlobalPixels(point, zoom) {
        var radius = 6378137;
        var equator = 2 * Math.PI * radius;
        var subequator = 1 / equator;
        var pixelsPerMeter = 256 * subequator;
        var halfEquator = equator / 2;
        var currentZoom = 0;
    
        if (zoom != currentZoom) {
            pixelsPerMeter = Math.pow(2, zoom + 8) * subequator;
            currentZoom = zoom;
        }
    
        var mercatorCoords = geoToMercator(point);
        return [
            (halfEquator + mercatorCoords[0]) * pixelsPerMeter,
            (halfEquator - mercatorCoords[1]) * pixelsPerMeter
        ];
    }
    
    function geoToMercator(geo) {
        return [
            longitudeToX(geo[0]),
            latitudeToY(geo[1])
        ];
    }
    ;
    
    function longitudeToX(lng) {
        var radius = 6378137;
        var c_pi180 = Math.PI / 180;
        var longitude = cycleRestrict(lng * c_pi180, -Math.PI, Math.PI);
        return radius * longitude;
    }
    
    function latitudeToY(lat) {
        var radius = 6378137;
        var e = 0.0818191908426;
        var c_pi180 = Math.PI / 180;
        var c_180pi = 180 / Math.PI;
        var epsilon = 1e-10;
        // epsilon чтобы не получить (-)Infinity
        var latitude = restrict(lat, -90 + epsilon, 90 - epsilon) * c_pi180;
        var esinLat = e * Math.sin(latitude);
    
        // Для широты -90 получается 0, и в результате по широте выходит -Infinity
        var tan_temp = Math.tan(Math.PI * 0.25 + latitude * 0.5);
        var pow_temp = Math.pow(Math.tan(Math.PI * 0.25 + Math.asin(esinLat) * 0.5), e);
        var U = tan_temp / pow_temp;
    
        return radius * Math.log(U);
    }
    
    function restrict(value, min, max) {
        return Math.max(Math.min(value, max), min);
    }
    function cycleRestrict(value, min, max) {
        if (value == Number.POSITIVE_INFINITY) {
            return max;
        } else if (value == Number.NEGATIVE_INFINITY) {
            return min;
        }
        return value - Math.floor((value - min) / (max - min)) * (max - min);
    }
    function calculateWorldSize(zoom) {
        return Math.pow(2, zoom + 8);
    }