Google지도 API V3에서 중심점을 오프셋하는 방법


78

지역의 일부를 덮는 반투명 패널이있는 Google지도가 있습니다. 지도에서 부분적으로 가려진 부분을 고려하여지도의 중심점을 조정하고 싶습니다. 아래 이미지를 참조하십시오. 이상적으로는 십자선과 핀이 배치되는 위치가지도의 중심점이됩니다.

그게 말이 되길 바랍니다.

이유는 간단합니다. 확대 / 축소 할 때 50 % 50 %가 아닌 십자선 위에지도를 중앙에 배치해야합니다. 또한지도에 마커를 플로팅하고 순서대로 이동합니다. 지도가 중심에있을 때 오프셋 위치에 있어야합니다.

미리 감사드립니다!

내가 만들고있는지도의 모형


질문을 명확히해야합니다. "부분적으로 가려진 맵 부분 고려"는 무엇을 의미합니까? 그리고 "50 %가 아닌 50 %"는 무엇을 의미합니까? 돕고 싶지만 달성하려는 목표를 파악할 수 없습니다.
Sean Mickey

안녕하세요, 당신은 내가 경로 라인과 함께 이동 계략 라인을 만들기위한 몇 가지 도움이 필요하기 때문에 동작하는 예제의 URL을 적어주세요 수
SP 싱

답변:


84

관련 이전 답변 을 찾으면 특별히 어렵지 않습니다 .

지도의 중심을 세계 좌표로 변환하고, 원하는 위치에 겉보기 중심을 배치하기 위해지도의 중심이 필요한 위치를 찾은 다음 실제 중심을 사용하여지도의 중심을 다시 조정해야합니다 .

API는 항상 뷰포트의 중앙에지도를 중앙에 위치 시키므로 사용 map.getCenter()하는 경우 명백한 중심이 아닌 실제 중심을 반환 하므로주의해야합니다 . API를 오버로드하여 해당 메서드 getCenter()setCenter()메서드를 대체 할 수 있다고 생각하지만 그렇게 하지 않았습니다.

아래 코드. 온라인 예 . 이 예에서 버튼을 클릭하면지도의 중심 (도로 교차로가 있음)이 100px 아래로 200px 왼쪽으로 이동합니다.

function offsetCenter(latlng, offsetx, offsety) {

    // latlng is the apparent centre-point
    // offsetx is the distance you want that point to move to the right, in pixels
    // offsety is the distance you want that point to move upwards, in pixels
    // offset can be negative
    // offsetx and offsety are both optional

    var scale = Math.pow(2, map.getZoom());

    var worldCoordinateCenter = map.getProjection().fromLatLngToPoint(latlng);
    var pixelOffset = new google.maps.Point((offsetx/scale) || 0,(offsety/scale) ||0);

    var worldCoordinateNewCenter = new google.maps.Point(
        worldCoordinateCenter.x - pixelOffset.x,
        worldCoordinateCenter.y + pixelOffset.y
    );

    var newCenter = map.getProjection().fromPointToLatLng(worldCoordinateNewCenter);

    map.setCenter(newCenter);

}

페이지가로드 될 때이 코드를 실행하여 처음 볼 때지도가 오프셋으로 표시되도록 할 수 있습니까?
Darren Cook

@DarrenCook : 물론입니다. 대신 이것을 사용하십시오 map.setCenter(). 이 코드가하는 일은지도가하는 일을 재 계산 한 다음 자체 map.setCenter().
Andrew Leach

감사합니다 Andrew, 그래서 내 코드에서 addMarkerFromAdress (latLng, title)를 가져 와서 offsetCenter (latlng, offsetx, offsety) 함수로 대체했는데 모두 여전히 작동하지만 offsety (243px) 값을 함수에 어떻게 가져 옵니까?
Darren Cook

27
더 나은 사용map.panBy(250, 0);
Alexander Shvetsov

2
나는 것을 발견 nwVAR는이 스크립트에 의해 사용되지 않습니다 내 사용의 경우에는 적어도 어떤 문제없이 제거 할 수있다. 나는 이미 경계를 설정하는 많은 다른 방법이 있기 때문에 이것을 단독으로 시도하지 않았습니다.
hellatan

70

또한 maps 객체의 panBy (x : number, y : number) 함수를 살펴보십시오.

문서는 함수에 대해 다음과 같이 언급합니다.

지정된 거리 (픽셀)만큼지도 중심을 변경합니다. 거리가지도의 너비와 높이보다 작 으면 전환이 부드럽게 애니메이션됩니다. 지도 좌표계는 서쪽에서 동쪽 (x 값)과 북쪽에서 남쪽 (y 값)으로 증가합니다.

다음과 같이 사용하십시오.

mapsObject.panBy(200, 100)

7
이 방법에 대해 항상 애니메이션을 비활성화하는 방법을 아는 사람이 있습니까?
Ash

이것이 답이 틀림 없다. 완벽하고 쉽게 작동했습니다. 감사.
Developer107

10

다음은 픽셀 대신 백분율을 사용할 수 있으므로 반응 형 디자인에서 더 유용 할 수있는 더 간단한 방법입니다. 세계 좌표도없고 LatLngs to Points도 없습니다!

var center;  // a latLng
var offsetX = 0.25; // move center one quarter map width left
var offsetY = 0.25; // move center one quarter map height down

var span = map.getBounds().toSpan(); // a latLng - # of deg map spans

var newCenter = { 
    lat: center.lat() + span.lat()*offsetY,
    lng: center.lng() + span.lng()*offsetX
};

map.panTo(newCenter);  // or map.setCenter(newCenter);

InfoBox에서 이것을 시도했고 작동합니다! (pixelOffset은 여전히 ​​수정해야 함)
시작

1
고려에 솔루션의 단순성과 복용 반응 디자인 모두 좋은 대답
다니엘 TONON

1
나는 캐치되지 않는 형식 오류를 얻을 : 부동산 'toSpan'정의의 읽을 수 없습니다
마이크 Kormendy

newCenter lat long을 얻을 수 있습니까?
씨도 망치고

1
동일한 작업에서 확대 / 축소를 변경해야하는 경우에는 작동하지 않습니다. 또한 목적지 (신규) 센터가 현재 센터에서 지리적으로 멀리 떨어져 있으면 정확하게 작동하지 않습니다. 동일한 확대 / 축소 수준에서도 화면 너비의 0.25는 사용 된 메르카토르 투영법으로 인해 노르웨이와 적도에서 다른 거리를 나타냅니다. 최종 좌표를 투영하는 것이 더 강력합니다.
pscl


8

또 다른 가장 간단한 해결책을 찾았습니다. fitBounds 메서드를 사용하는 경우 선택적 두 번째 인수를 해당 메서드에 전달할 수 있습니다. 이 인수는 패딩이며 경계를 맞추는 동안 고려됩니다.

// pass single side:
map.fitBounds(bounds, { left: 1000 })

// OR use Number:
map.fitBounds(bounds, 20)

추가 읽기 : 공식 문서 .


이것은 완벽 해요!
monchisan

6

앤드류가 답입니다. 그러나 제 경우에는 map.getBounds ()가 계속해서 undefined를 반환했습니다. bounds_changed 이벤트를 기다리면서 수정 한 다음 함수를 호출하여 중심을 오프셋했습니다. 이렇게 :

var center_moved = false;
google.maps.event.addListener(map, 'bounds_changed', function() {
  if(!center_moved){
    offsetCenter(map.getCenter(), 250, -200);
    center_moved = true; 
  }
});

1
이 대답은 Andrew의 대답 (뒤에 배치됨)과 결합하여 저에게 해결책이었습니다. Nahuel 감사합니다!
Mike Kormendy

5

오래된 질문입니다. 하지만 CSS 중심적인 방법은 어떻습니까?

http://codepen.io/eddyblair/pen/VjpNQQ

내가 한 일은 :

  • 컨테이너에지도와 오버레이를 overflow: hidden

  • 오버레이를 position: absolute

  • 음수를 설정하여 오버레이 너비 (패딩 및 오프셋 포함)만큼 지도의 겉보기 너비 를 확장했습니다 margin-left.

  • 그런 다음 https://www.google.com/permissions/geoguidelines/attr-guide.html 을 준수하기 위해 위젯 및 속성을 배치했습니다 div.

이렇게하면지도의 중심이 원하는 영역의 중심과 일직선이됩니다. js는 표준 맵 js입니다.

스트리트 뷰의 아이콘 위치 변경은 독자를위한 연습입니다. :)


당신은 왼쪽, 단지 변화 라인에 오버레이 원하는 경우 24 margin-leftmargin-right라인 (32) right에을 left.


저는이 접근 방식을 정말 좋아합니다. Angular / javascript를 멋지고 깨끗하게 유지합니다.
Various Artist

감사합니다 :) 물론 이것은 Google이 스타일을 변경할 때마다 업데이트해야합니다. 속성과 컨트롤 등을 이동하는 CSS 앞에 주석을 달았습니다. 수정이 필요한 경우 어떻게했는지에 대한 아이디어를 제공해야합니다. 스트리트 뷰를 정렬하려면 추가 CSS를 만들어야합니다.
Mardoxx

3

광범위한 검색 후 나는 줌을 포함하여 이것을 수행하는 방법을 찾을 수 없었습니다. 고맙게도 영리한 녀석 이 알아 냈습니다. 여기바이올린도 있습니다

'use strict';

const TILE_SIZE = {
  height: 256,
  width: 256
}; // google World tile size, as of v3.22
const ZOOM_MAX = 21; // max google maps zoom level, as of v3.22
const BUFFER = 15; // edge buffer for fitting markers within viewport bounds

const mapOptions = {
  zoom: 14,
  center: {
    lat: 34.075328,
    lng: -118.330432
  },
  options: {
    mapTypeControl: false
  }
};
const markers = [];
const mapDimensions = {};
const mapOffset = {
  x: 0,
  y: 0
};
const mapEl = document.getElementById('gmap');
const overlayEl = document.getElementById('overlay');
const gmap = new google.maps.Map(mapEl, mapOptions);

const updateMapDimensions = () => {
  mapDimensions.height = mapEl.offsetHeight;
  mapDimensions.width = mapEl.offsetWidth;
};

const getBoundsZoomLevel = (bounds, dimensions) => {
  const latRadian = lat => {
    let sin = Math.sin(lat * Math.PI / 180);
    let radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
    return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
  };
  const zoom = (mapPx, worldPx, fraction) => {
    return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
  };
  const ne = bounds.getNorthEast();
  const sw = bounds.getSouthWest();
  const latFraction = (latRadian(ne.lat()) - latRadian(sw.lat())) / Math.PI;
  const lngDiff = ne.lng() - sw.lng();
  const lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
  const latZoom = zoom(dimensions.height, TILE_SIZE.height, latFraction);
  const lngZoom = zoom(dimensions.width, TILE_SIZE.width, lngFraction);
  return Math.min(latZoom, lngZoom, ZOOM_MAX);
};

const getBounds = locations => {
  let northeastLat;
  let northeastLong;
  let southwestLat;
  let southwestLong;
  locations.forEach(function(location) {
    if (!northeastLat) {
      northeastLat = southwestLat = location.lat;
      southwestLong = northeastLong = location.lng;
      return;
    }
    if (location.lat > northeastLat) northeastLat = location.lat;
    else if (location.lat < southwestLat) southwestLat = location.lat;
    if (location.lng < northeastLong) northeastLong = location.lng;
    else if (location.lng > southwestLong) southwestLong = location.lng;
  });
  const northeast = new google.maps.LatLng(northeastLat, northeastLong);
  const southwest = new google.maps.LatLng(southwestLat, southwestLong);
  const bounds = new google.maps.LatLngBounds();
  bounds.extend(northeast);
  bounds.extend(southwest);
  return bounds;
};

const zoomWithOffset = shouldZoom => {
  const currentzoom = gmap.getZoom();
  const newzoom = shouldZoom ? currentzoom + 1 : currentzoom - 1;
  const offset = {
    x: shouldZoom ? -mapOffset.x / 4 : mapOffset.x / 2,
    y: shouldZoom ? -mapOffset.y / 4 : mapOffset.y / 2
  };
  const newCenter = offsetLatLng(gmap.getCenter(), offset.x, offset.y);
  if (shouldZoom) {
    gmap.setZoom(newzoom);
    gmap.panTo(newCenter);
  } else {
    gmap.setCenter(newCenter);
    gmap.setZoom(newzoom);
  }
};

const setMapBounds = locations => {
  updateMapDimensions();
  const bounds = getBounds(locations);
  const dimensions = {
    width: mapDimensions.width - mapOffset.x - BUFFER * 2,
    height: mapDimensions.height - mapOffset.y - BUFFER * 2
  };
  const zoomLevel = getBoundsZoomLevel(bounds, dimensions);
  gmap.setZoom(zoomLevel);
  setOffsetCenter(bounds.getCenter());
};

const offsetLatLng = (latlng, offsetX, offsetY) => {
  offsetX = offsetX || 0;
  offsetY = offsetY || 0;
  const scale = Math.pow(2, gmap.getZoom());
  const point = gmap.getProjection().fromLatLngToPoint(latlng);
  const pixelOffset = new google.maps.Point((offsetX / scale), (offsetY / scale));
  const newPoint = new google.maps.Point(
    point.x - pixelOffset.x,
    point.y + pixelOffset.y
  );
  return gmap.getProjection().fromPointToLatLng(newPoint);
};

const setOffsetCenter = latlng => {
  const newCenterLatLng = offsetLatLng(latlng, mapOffset.x / 2, mapOffset.y / 2);
  gmap.panTo(newCenterLatLng);
};

const locations = [{
  name: 'Wilshire Country Club',
  lat: 34.077796,
  lng: -118.331151
}, {
  name: '301 N Rossmore Ave',
  lat: 34.077146,
  lng: -118.327805
}, {
  name: '5920 Beverly Blvd',
  lat: 34.070281,
  lng: -118.331831
}];

locations.forEach(function(location) {
  let marker = new google.maps.Marker({
    position: new google.maps.LatLng(location.lat, location.lng),
    title: location.name
  })
  marker.setMap(gmap);
  markers.push(marker);
});

mapOffset.x = overlayEl.offsetWidth;

document.zoom = bool => zoomWithOffset(bool);
document.setBounds = () => setMapBounds(locations);
section {
  height: 180px;
  margin-bottom: 15px;
  font-family: sans-serif;
  color: grey;
}
figure {
  position: relative;
  margin: 0;
  width: 100%;
  height: 100%;
}
figcaption {
  position: absolute;
  left: 15px;
  top: 15px;
  width: 120px;
  padding: 15px;
  background: white;
  box-shadow: 0 2px 5px rgba(0, 0, 0, .3);
}
gmap {
  display: block;
  height: 100%;
}
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>

<section>
  <figure>
    <gmap id="gmap"></gmap>
    <figcaption id="overlay">
      <h4>Tile Overlay</h4>
      <p>To be avoided by the map!</p>
    </figcaption>
  </figure>
</section>
<button onclick="zoom(true)">zoom in</button>
<button onclick="zoom(false)">zoom out</button>
<button onclick="setBounds()">set bounds</button>


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.