getBoundsZoomLevel()V2 API 와 비슷한 Google Maps V3 API를 사용하여 지정된 범위의 확대 / 축소 수준을 계산하는 방법을 찾고 있습니다.
내가하고 싶은 일은 다음과 같습니다.
// These are exact bounds previously captured from the map object
var sw = new google.maps.LatLng(42.763479, -84.338918);
var ne = new google.maps.LatLng(42.679488, -84.524313);
var bounds = new google.maps.LatLngBounds(sw, ne);
var zoom = // do some magic to calculate the zoom level
// Set the map to these exact bounds
map.setCenter(bounds.getCenter());
map.setZoom(zoom);
// NOTE: fitBounds() will not work
불행히도, 나는 fitBounds()특정 유스 케이스에 메소드를 사용할 수 없습니다 . 맵에서 마커를 맞추는 데는 효과가 있지만 정확한 범위를 설정하는 데는 효과가 없습니다. 이 fitBounds()방법을 사용할 수없는 이유는 다음과 같습니다 .
map.fitBounds(map.getBounds()); // not what you expect
답변
Google 그룹에서도 비슷한 질문이 있습니다. http://groups.google.com/group/google-maps-js-api-v3/browse_thread/thread/e6448fc197c3c892
확대 / 축소 수준은 개별 단계이며 각 단계에서 배율이 두 배가됩니다. 따라서 일반적으로 특정 맵 크기로 운이 좋지 않은 한 정확하게 원하는 범위에 맞출 수 없습니다.
또 다른 문제는 측면 길이 사이의 비율입니다. 예를 들어 사각형 맵 내부의 얇은 사각형에 경계를 정확하게 맞출 수 없습니다.
정확한 범위를 맞추는 방법에 대한 쉬운 대답은 없습니다.지도 div의 크기를 기꺼이 변경하더라도 변경할 크기와 해당 줌 레벨을 선택해야하기 때문에 (대략 말하면 크게 또는 작게 만들까요) 현재보다?).
확대 / 축소를 저장하지 않고 계산해야하는 경우 다음과 같은 트릭을 수행해야합니다.
메르카토르 투영법은 위도를 왜곡하지만 경도의 차이는 항상지도 너비의 동일한 비율 (각도 / 360도)을 나타냅니다. 확대 / 축소 0에서 전 세계지도는 256×256 픽셀이며 각 수준을 확대 / 축소하면 너비와 높이가 두 배가됩니다. 약간의 대수 후에 맵의 너비를 픽셀 단위로 알고 있다면 다음과 같이 줌을 계산할 수 있습니다. 경도가 둘러싸 기 때문에 각도가 양수인지 확인해야합니다.
var GLOBE_WIDTH = 256; // a constant in Google's map projection
var west = sw.lng();
var east = ne.lng();
var angle = east - west;
if (angle < 0) {
angle += 360;
}
var zoom = Math.round(Math.log(pixelWidth * 360 / angle / GLOBE_WIDTH) / Math.LN2);
답변
그의 대답에 대해 Giles Gardam에게 감사하지만 위도가 아닌 경도 만 다룹니다. 완벽한 솔루션은 위도에 필요한 확대 / 축소 수준과 경도에 필요한 확대 / 축소 수준을 계산 한 다음 둘 중 더 작은 값을 가져와야합니다.
위도와 경도를 모두 사용하는 함수는 다음과 같습니다.
function getBoundsZoomLevel(bounds, mapDim) {
var WORLD_DIM = { height: 256, width: 256 };
var ZOOM_MAX = 21;
function latRad(lat) {
var sin = Math.sin(lat * Math.PI / 180);
var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
}
function zoom(mapPx, worldPx, fraction) {
return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
}
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var latFraction = (latRad(ne.lat()) - latRad(sw.lat())) / Math.PI;
var lngDiff = ne.lng() - sw.lng();
var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
var latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);
var lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);
return Math.min(latZoom, lngZoom, ZOOM_MAX);
}
매개 변수 :
“bounds”매개 변수 값은 google.maps.LatLngBounds객체 여야 합니다.
“mapDim”매개 변수 값은 맵을 표시하는 DOM 요소의 높이와 너비를 나타내는 “height”및 “width”속성이있는 객체 여야합니다. 패딩을 보장하려면이 값을 줄이십시오. 즉, 경계 내의지도 마커가지도의 가장자리에 너무 가까이 있지 않도록 할 수 있습니다.
jQuery 라이브러리를 사용하는 경우 mapDim다음과 같이 값을 얻을 수 있습니다.
var $mapDiv = $('#mapElementId');
var mapDim = { height: $mapDiv.height(), width: $mapDiv.width() };
프로토 타입 라이브러리를 사용하는 경우 mapDim 값은 다음과 같이 얻을 수 있습니다.
var mapDim = $('mapElementId').getDimensions();
반환 값 :
반환 값은 여전히 전체 범위를 표시하는 최대 줌 레벨입니다. 이 값은 0최대 및 최대 줌 레벨 사이 입니다.
최대 확대 / 축소 수준은 21입니다 (Google Maps API v2의 경우 19 일뿐입니다).
설명:
Google지도는 메르카토르 투영법을 사용합니다. 메르카토르 투영법에서 경도선은 동일 간격이지만 위 도선은 동일하지 않습니다. 위도 선 사이의 거리는 적도에서 극점으로 갈수록 증가합니다. 실제로 거리는 극점에 도달 할 때 무한대로 향하는 경향이 있습니다. 그러나 Google지도지도에는 북쪽으로 약 85도 이상 또는 남쪽으로 약 -85도 이하의 위도가 표시되지 않습니다. ( 참고 ) (실제 컷오프는 +/- 85.05112877980658 도로 계산합니다.)
이렇게하면 경도보다 경도에 대한 경계의 분수 계산이 더 복잡해집니다. 위도 비율을 계산하기 위해 Wikipedia 의 공식을 사용했습니다 . 나는 이것이 Google지도에서 사용하는 예상과 일치한다고 가정합니다. 결국, 내가 링크 한 Google지도 문서 페이지에는 동일한 Wikipedia 페이지에 대한 링크가 포함되어 있습니다.
기타 사항 :
답변
API 버전 3의 경우 간단하고 작동합니다.
var latlngList = [];
latlngList.push(new google.maps.LatLng(lat, lng));
var bounds = new google.maps.LatLngBounds();
latlngList.each(function(n) {
bounds.extend(n);
});
map.setCenter(bounds.getCenter()); //or use custom center
map.fitBounds(bounds);
그리고 몇 가지 선택적인 요령 :
//remove one zoom level to ensure no marker is on the edge.
map.setZoom(map.getZoom() - 1);
// set a minimum zoom
// if you got only 1 marker or all markers are on the same address map will be zoomed too much.
if(map.getZoom() > 15){
map.setZoom(15);
}
답변
다음은 Kotlin 버전의 함수입니다.
fun getBoundsZoomLevel(bounds: LatLngBounds, mapDim: Size): Double {
val WORLD_DIM = Size(256, 256)
val ZOOM_MAX = 21.toDouble();
fun latRad(lat: Double): Double {
val sin = Math.sin(lat * Math.PI / 180);
val radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return max(min(radX2, Math.PI), -Math.PI) /2
}
fun zoom(mapPx: Int, worldPx: Int, fraction: Double): Double {
return floor(Math.log(mapPx / worldPx / fraction) / Math.log(2.0))
}
val ne = bounds.northeast;
val sw = bounds.southwest;
val latFraction = (latRad(ne.latitude) - latRad(sw.latitude)) / Math.PI;
val lngDiff = ne.longitude - sw.longitude;
val lngFraction = if (lngDiff < 0) { (lngDiff + 360) } else { (lngDiff / 360) }
val latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);
val lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);
return minOf(latZoom, lngZoom, ZOOM_MAX)
}
답변
덕분에 폴리 라인을 올바르게 표시하는 데 가장 적합한 줌 배율을 찾는 데 많은 도움이되었습니다. 추적해야 할 점 중 최대 및 최소 좌표를 찾고 경로가 매우 “수직”인 경우 몇 줄의 코드를 추가했습니다.
var GLOBE_WIDTH = 256; // a constant in Google's map projection
var west = <?php echo $minLng; ?>;
var east = <?php echo $maxLng; ?>;
*var north = <?php echo $maxLat; ?>;*
*var south = <?php echo $minLat; ?>;*
var angle = east - west;
if (angle < 0) {
angle += 360;
}
*var angle2 = north - south;*
*if (angle2 > angle) angle = angle2;*
var zoomfactor = Math.round(Math.log(960 * 360 / angle / GLOBE_WIDTH) / Math.LN2);
실제로 이상적인 줌 배율은 zoomfactor-1입니다.
답변
다른 모든 대답은 하나 또는 다른 상황 세트 (지도 너비 / 높이, 경계 너비 / 높이 등)에 문제가있는 것처럼 보이므로 여기에 대답을 넣을 것이라고 생각했습니다 …
여기에 매우 유용한 자바 스크립트 파일이 있습니다 : http://www.polyarc.us/adjust.js
나는 이것을 기본으로 사용했습니다.
var com = com || {};
com.local = com.local || {};
com.local.gmaps3 = com.local.gmaps3 || {};
com.local.gmaps3.CoordinateUtils = new function() {
var OFFSET = 268435456;
var RADIUS = OFFSET / Math.PI;
/**
* Gets the minimum zoom level that entirely contains the Lat/Lon bounding rectangle given.
*
* @param {google.maps.LatLngBounds} boundary the Lat/Lon bounding rectangle to be contained
* @param {number} mapWidth the width of the map in pixels
* @param {number} mapHeight the height of the map in pixels
* @return {number} the minimum zoom level that entirely contains the given Lat/Lon rectangle boundary
*/
this.getMinimumZoomLevelContainingBounds = function ( boundary, mapWidth, mapHeight ) {
var zoomIndependentSouthWestPoint = latLonToZoomLevelIndependentPoint( boundary.getSouthWest() );
var zoomIndependentNorthEastPoint = latLonToZoomLevelIndependentPoint( boundary.getNorthEast() );
var zoomIndependentNorthWestPoint = { x: zoomIndependentSouthWestPoint.x, y: zoomIndependentNorthEastPoint.y };
var zoomIndependentSouthEastPoint = { x: zoomIndependentNorthEastPoint.x, y: zoomIndependentSouthWestPoint.y };
var zoomLevelDependentSouthEast, zoomLevelDependentNorthWest, zoomLevelWidth, zoomLevelHeight;
for( var zoom = 21; zoom >= 0; --zoom ) {
zoomLevelDependentSouthEast = zoomLevelIndependentPointToMapCanvasPoint( zoomIndependentSouthEastPoint, zoom );
zoomLevelDependentNorthWest = zoomLevelIndependentPointToMapCanvasPoint( zoomIndependentNorthWestPoint, zoom );
zoomLevelWidth = zoomLevelDependentSouthEast.x - zoomLevelDependentNorthWest.x;
zoomLevelHeight = zoomLevelDependentSouthEast.y - zoomLevelDependentNorthWest.y;
if( zoomLevelWidth <= mapWidth && zoomLevelHeight <= mapHeight )
return zoom;
}
return 0;
};
function latLonToZoomLevelIndependentPoint ( latLon ) {
return { x: lonToX( latLon.lng() ), y: latToY( latLon.lat() ) };
}
function zoomLevelIndependentPointToMapCanvasPoint ( point, zoomLevel ) {
return {
x: zoomLevelIndependentCoordinateToMapCanvasCoordinate( point.x, zoomLevel ),
y: zoomLevelIndependentCoordinateToMapCanvasCoordinate( point.y, zoomLevel )
};
}
function zoomLevelIndependentCoordinateToMapCanvasCoordinate ( coordinate, zoomLevel ) {
return coordinate >> ( 21 - zoomLevel );
}
function latToY ( lat ) {
return OFFSET - RADIUS * Math.log( ( 1 + Math.sin( lat * Math.PI / 180 ) ) / ( 1 - Math.sin( lat * Math.PI / 180 ) ) ) / 2;
}
function lonToX ( lon ) {
return OFFSET + RADIUS * lon * Math.PI / 180;
}
};
분명히 이것을 정리하거나 필요한 경우 최소화 할 수 있지만 이해하기 쉽도록 변수 이름을 길게 유지했습니다.
OFFSET의 출처가 궁금하다면 268435456은 줌 레벨 21에서 지구 둘레의 절반입니다. ( http://www.appelsiini.net/2008/11/introduction-to-marker-clustering-with-google 에 따르면 -maps ).
답변
Valerio는 자신의 솔루션에 거의 맞지만 논리적 실수가 있습니다.
360을 음수로 추가하기 전에 먼저 angle2가 angle보다 큰지 확인해야합니다.
그렇지 않으면 항상 각도보다 큰 값을 갖습니다
올바른 해결책은 다음과 같습니다.
var west = calculateMin(data.longitudes);
var east = calculateMax(data.longitudes);
var angle = east - west;
var north = calculateMax(data.latitudes);
var south = calculateMin(data.latitudes);
var angle2 = north - south;
var zoomfactor;
var delta = 0;
var horizontal = false;
if(angle2 > angle) {
angle = angle2;
delta = 3;
}
if (angle < 0) {
angle += 360;
}
zoomfactor = Math.floor(Math.log(960 * 360 / angle / GLOBE_WIDTH) / Math.LN2) - 2 - delta;
델타가 있습니다. 왜냐하면 높이보다 너비가 더 큽니다.
