카메라 및 뷰

Android용 Maps SDK의 지도는 손쉬운 동작으로 기울이기 및 회전이 가능하므로 사용자가 자신에게 알맞은 방향으로 지도를 조정할 수 있습니다. 벡터 기반 지도 타일의 접지면이 더 작기 때문에 모든 확대/축소 수준에서 거의 지연 없이 지도를 이동하거나 시점을 변경할 수 있습니다.

코드 샘플

GitHub의 ApiDemos 저장소에는 카메라 기능을 보여주는 샘플이 포함되어 있습니다.

도입

웹용 Google 지도와 마찬가지로 Android용 Maps SDK에서도 메르카토르 투영법을 사용하여 기기의 화면(평면)에 지표면(구)을 표시합니다. 세계는 한 치의 틈도 없이 둥글게 이어져 있으므로 지도는 동서로 무한하게 반복됩니다. 북쪽과 남쪽 방향으로 지도는 북쪽으로 약 85도, 남쪽으로 약 85도로 제한됩니다.

참고: 메르카토르 투영법은 경도로는 너비가 한정되어 있지만 위도로는 높이에 제한이 없습니다. Google에서는 메르카토르 투영법을 활용하여 약 +/-85도로 기본 지도 이미지를 잘라 결과로 표시되는 지도 모양을 정사각형으로 만듭니다. 이런 방식으로 타일 선택에 더 쉬운 로직을 적용하게 됩니다.

Android용 Maps SDK를 사용하면 지도의 카메라를 수정하여 지도의 사용자 시점을 변경할 수 있습니다.

카메라를 변경해도 추가한 마커, 오버레이 또는 다른 그래픽은 변경되지 않지만 추가한 항목이 새로운 뷰에 더 잘 맞도록 변경하는 것이 좋습니다.

지도에서 사용자 동작을 수신할 수 있으므로 사용자의 요청에 따라 지도를 변경할 수 있습니다. 예를 들어 콜백 메서드 OnMapClickListener.onMapClick()은 지도를 한 번 탭하면 응답합니다. 이 메서드는 탭 위치의 경도와 위도를 수신하므로 그 지점으로 화면을 이동하거나 확대/축소하는 식으로 반응할 수 있습니다. 마커의 도움말 풍선을 탭하거나 마커를 드래그하는 동작에 대한 응답에도 유사한 메서드를 사용할 수 있습니다.

카메라 이동을 수신할 수도 있으므로 카메라가 움직이기 시작하거나 현재 움직이고 있거나 움직임을 멈추면 앱에서 알림을 수신합니다. 자세한 내용은 카메라 변경 이벤트 가이드를 참고하세요.

지도상의 3D 빌딩

아래 캐나다 밴쿠버의 사진에 보이는 것처럼 확대해서 보면 많은 도시에 3D 빌딩이 표시됩니다. GoogleMap.setBuildingsEnabled(false)를 호출하여 3D 빌딩 표시를 중지할 수 있습니다.

캐나다 밴쿠버의 지도

카메라 위치

지도뷰는 평면을 내려다보는 카메라로 모델링됩니다. 카메라의 위치(및 이에 따른 지도의 렌더링)는 대상(위도/경도 위치), 방위, 기울기, 확대/축소 등의 속성으로 지정됩니다.

카메라 속성 다이어그램

대상(위치)

카메라 타겟은 지도 중앙의 위치에 해당되며 위도와 경도 좌표로 지정됩니다.

방위(방향)

카메라 방위는 지도의 수직선이 가리키는 방향을 나타내며 북쪽에서 시계 방향으로 측정됩니다(단위: 도). 차량 운전자는 종종 도로 지도를 돌려서 자신의 이동 방향에 맞추는 반면, 지도와 나침반을 사용하는 도보 여행자는 대체로 수직선이 북쪽을 향하도록 지도의 방향을 바꿉니다. Maps API를 사용하면 지도의 정렬 또는 방위를 변경할 수 있습니다. 예를 들어 방위가 90도인 경우 지도에서 위쪽 방향은 정동향을 가리킵니다.

기울기(시야각)

기울기는 지도의 중앙 지점 바로 위쪽과 지면 사이의 원호에서 카메라 위치를 정의하며 천저(카메라 바로 아래를 가리키는 방향)에서부터 측정됩니다(단위: 도). 시야각을 변경하면 지도가 원근법으로 표시되어 멀리 있는 지형지물은 더 작게 표시되고 가까이 있는 지형지물은 더 크게 표시됩니다. 다음 그림은 이러한 원근법을 보여 줍니다.

아래 이미지에서 시야각은 0도입니다. 첫 번째 이미지는 이를 도식으로 보여줍니다. 위치 1은 카메라 위치에 해당되며 위치 2는 현재 지도의 위치에 해당됩니다. 그 결과 나타나는 지도는 아래와 같습니다.

확대/축소 수준 18, 카메라가 시야각 0도로 배치된 지도의 스크린샷
카메라의 기본 시야각으로 표시되는 지도
0도의 각도로 지도 위치 바로 위에 배치된 카메라의 기본 위치를 보여주는 다이어그램
카메라의 기본 시야각

아래 이미지에서 시야각은 45도입니다. 카메라는 45도로 기울어지지 않으며 그 대신 바로 위(0도)와 지상(90도) 사이의 원호를 따라 위치 3으로 절반쯤 이동합니다. 카메라는 여전히 지도의 중심점을 가리키지만 이제 위치 4에서 선으로 표시되는 영역이 보입니다.

확대/축소 수준 18, 카메라가 시야각 45도로 배치된 지도의 스크린샷
45도의 시야각으로 표시되는 지도
확대/축소 수준이 여전히 18로 설정된 상태에서 45도로 설정된 카메라의 시야각을 표시하는 다이어그램
45도의 카메라 시야각

이 스크린샷에서 지도의 중앙은 원래 지도와 동일한 지점에 위치하지만 지도 상단에서는 더 많은 지형지물이 표시됩니다. 시야각을 45도 이상으로 올리면 카메라와 지도 위치 사이의 지형지물이 그에 비례하여 크게 표시되는 반면, 지도 위치 너머에 있는 지형지물은 그에 비례하여 작게 표시되어 입체적 효과를 줍니다.

확대/축소

카메라의 확대/축소 수준이 지도의 축척을 결정합니다. 확대/축소 수준이 높을수록 더욱 상세한 화면을 볼 수 있는 반면, 확대/축소 수준이 낮을수록 더 넓은 영역을 화면에 표시할 수 있습니다. 확대/축소 수준이 0일 때 지도의 축척은 전 세계의 너비가 약 256dp(밀도 독립형 픽셀)로 결정됩니다.

확대/축소 수준을 1씩 올리면 화면에서 세계의 너비는 두 배가 됩니다. 따라서 확대/축소 수준이 N일 때 지구의 너비는 약 256 x 2N dp가 됩니다. 즉, 확대/축소 수준이 2라면 전 세계의 너비는 약 1024dp가 됩니다. 참고로 확대/축소 수준은 정수가 아니어도 됩니다. 지도에서 허용되는 확대/축소 수준의 범위는 위치, 지도 유형, 화면 크기 등 여러 가지 요소에 따라 달라집니다. 범위를 벗어난 모든 숫자는 그 다음으로 유효한 근사치로 변환됩니다. 이 값은 최소 확대/축소 수준 또는 최대 확대/축소 수준일 수 있습니다. 다음 목록은 각 확대/축소 수준에서 기대할 수 있는 세밀한 정도의 대략적인 수준을 보여줍니다.

  • 1: 세계
  • 5: 대륙
  • 10: 도시
  • 15: 거리
  • 20: 건물

다음 이미지는 다양한 확대/축소 수준의 시각적 모양을 보여줍니다.

확대/축소 수준이 5인 지도의 스크린샷
확대/축소 수준이 5인 지도
확대/축소 수준이 15인 지도의 스크린샷
확대/축소 수준이 15인 지도
확대/축소 수준이 20인 지도의 스크린샷
확대/축소 수준이 20인 지도

참고: 화면 크기 및 밀도로 인해 일부 기기에서는 가장 낮은 확대/축소 수준을 지원하지 않을 수 있습니다. GoogleMap.getMinimumZoomLevel()을 사용하여 지도에 적용 가능한 최소 확대/축소 수준을 가져옵니다. 표시 영역에 전 세계를 표시해야 할 경우에는 라이트 모드를 사용하는 것이 좋습니다.

카메라 이동

지도 API를 사용하면 지도에 세계의 어느 부분이 보이게 할 지 바꿀 수 있습니다. 지도를 움직이는 대신 카메라의 위치를 변경하면 지도에 표시되는 부분이 변경됩니다.

카메라를 변경할 때에는 위치를 바꾸면서 나타나는 카메라 움직임을 애니메이션으로 표시할 수 있습니다(옵션). 애니메이션은 현재 카메라 속성과 새 카메라 속성 사이에 삽입됩니다. 또한 애니메이션의 길이를 제어할 수도 있습니다.

카메라의 위치를 변경하려면 CameraUpdate를 사용하여 카메라를 이동할 위치를 지정해야 합니다. 지도 API를 사용하면 CameraUpdateFactory를 사용하여 여러 유형의 CameraUpdate를 만들 수 있습니다. 사용할 수 있는 옵션은 다음과 같습니다.

확대/축소 수준 변경 및 최소/최대 확대/축소 설정

CameraUpdateFactory.zoomIn()CameraUpdateFactory.zoomOut()은 다른 모든 속성을 동일하게 유지하면서 확대/축소 수준을 1.0씩 변경하는 CameraUpdate를 제공합니다.

CameraUpdateFactory.zoomTo(float)는 다른 모든 속성을 동일하게 유지하면서 확대/축소 수준을 특정 값으로 변경하는 CameraUpdate를 제공합니다.

CameraUpdateFactory.zoomBy(float)CameraUpdateFactory.zoomBy(float, Point)는 확대/축소 수준을 특정 값만큼 증가시키고 값이 음수인 경우에는 반대로 감소시키는 CameraUpdate를 제공합니다. 후자는 화면의 특정 지점이 동일한 위치(위도/경도)에 유지되도록 고정하며 이러한 고정을 위해 카메라의 위치를 변경할 수도 있습니다.

선호하는 최소 또는 최대 확대/축소 수준을 설정해두는 것이 유용합니다. 예를 들어, 앱에서 관심 지점 주변의 특정 지역을 보여주거나 제한된 확대/축소 수준 세트로 맞춤 타일 오버레이를 사용한다면 이러한 설정은 사용자 환경을 제어하는 데 도움이 됩니다.

자바

private GoogleMap map;
    map.setMinZoomPreference(6.0f);
    map.setMaxZoomPreference(14.0f);
      

Kotlin

private lateinit var map: GoogleMap

    map.setMinZoomPreference(6.0f)
    map.setMaxZoomPreference(14.0f)
      

API에서 사용자가 너무 낮거나 높은 수준까지 확대/축소하는 것을 허용하지 않도록 기술적으로 막을 수도 있습니다. 예를 들어, 위성 또는 지형은 기본 지도 타일보다 최대 확대/축소 수준이 낮을 수 있습니다.

카메라 위치 변경

일반적인 위치 변경을 위한 편리한 메서드가 두 가지 있습니다. CameraUpdateFactory.newLatLng(LatLng)는 다른 모든 속성은 유지하면서 카메라의 위도 및 경도를 변경하는 CameraUpdate를 제공합니다. CameraUpdateFactory.newLatLngZoom(LatLng, float)은 다른 모든 속성을 유지하면서 카메라의 위도, 경도, 확대/축소를 변경하는 CameraUpdate를 제공합니다.

카메라 위치를 최대한 유연하게 변경하려면 카메라를 특정 위치로 이동하는 CameraUpdate를 제공하는 CameraUpdateFactory.newCameraPosition(CameraPosition)을 사용합니다. CameraPositionnew CameraPosition()을 사용하거나 CameraPosition.Builder와 함께 new CameraPosition.Builder()를 사용하여 직접 가져올 수 있습니다.

화면 이동(스크롤)

CameraUpdateFactory.scrollBy(float, float)는 지도가 특정 픽셀 수만큼 이동하도록 카메라의 위도와 경도를 변경하는 CameraUpdate를 제공합니다. 양수 x값을 적용하면 카메라가 오른쪽으로 이동하여 지도가 왼쪽으로 이동한 것처럼 보입니다. 양수 y값을 적용하면 카메라가 아래로 이동하여 지도가 위로 올라간 것처럼 보입니다. 반대로 음수 x값을 적용하면 카메라가 왼쪽으로 이동하여 지도가 오른쪽으로 이동한 것처럼 보이며 음수 y값을 적용하면 카메라가 위로 이동하게 됩니다. 스크롤은 카메라의 현재 방향에 따라 진행됩니다. 예를 들어 카메라의 방위가 90도라면 동쪽이 '위'입니다.

경계 설정

지도의 경계 설정

전체 관심 지역이 최대한 큰 확대/축소 수준으로 표시되도록 카메라를 이동하는 것이 유용한 때가 많습니다. 예를 들어, 사용자의 현재 위치에서 5마일 이내에 있는 모든 주유소를 표시한다면 모두 화면에 보이도록 카메라를 이동하는 것이 좋습니다. 이렇게 하려면 먼저 화면에 표시할 LatLngBounds를 계산합니다. 그런 다음 CameraUpdateFactory.newLatLngBounds(LatLngBounds bounds, int padding)를 사용하여 CameraUpdate를 가져올 수 있으며 그러면 지정된 패딩(단위: 픽셀)을 고려하여 특정 LatLngBounds가 지도 내에 딱 들어 맞도록 카메라 위치가 변경됩니다. 반환된 CameraUpdate를 적용하면 주어진 경계와 지도 가장자리 사이의 간격(단위: 픽셀)이 최소한 지정된 패딩 이상의 여유를 둘 수 있습니다. 지도의 기울기 및 방위는 모두 0으로 설정됩니다.

자바

LatLngBounds australiaBounds = new LatLngBounds(
    new LatLng(-44, 113), // SW bounds
    new LatLng(-10, 154)  // NE bounds
);
map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0));
      

Kotlin

val australiaBounds = LatLngBounds(
    LatLng((-44.0), 113.0),  // SW bounds
    LatLng((-10.0), 154.0) // NE bounds
)
map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0))
      

영역 내에 카메라 중심 맞추기

경우에 따라서는 경계선을 지나치게 포함하는 대신 경계 내에서 카메라를 중심에 배치하는 것이 좋습니다. 예를 들면 확대/축소를 일정하게 유지하면서 특정 국가에 카메라의 중심을 맞추는 경우를 생각해 봅시다. 이 경우 LatLngBounds를 생성하고 CameraUpdateFactory.newLatLngZoom(LatLng latLng, float zoom)LatLngBounds.getCenter() 메서드와 함께 사용하여 비슷한 메서드를 활용할 수 있습니다. getCenter() 메서드는 LatLngBounds의 지리적 중심을 반환합니다.

자바

LatLngBounds australiaBounds = new LatLngBounds(
    new LatLng(-44, 113), // SW bounds
    new LatLng(-10, 154)  // NE bounds
);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(australiaBounds.getCenter(), 10));
      

Kotlin

val australiaBounds = LatLngBounds(
    LatLng((-44.0), 113.0),  // SW bounds
    LatLng((-10.0), 154.0) // NE bounds
)
map.moveCamera(CameraUpdateFactory.newLatLngZoom(australiaBounds.center, 10f))
      

메서드의 오버로드인 newLatLngBounds(boundary, width, height, padding)를 사용하면 지도의 치수와 일치하도록 직사각형의 너비와 높이를 픽셀 단위로 지정할 수 있습니다. 직사각형의 중심은 지도 뷰의 중심과 같은 곳에 위치합니다(지정된 치수가 지도 뷰의 치수와 같다면 직사각형은 지도 뷰와 일치하게 됩니다). 반환된 CameraUpdate는 지정된 LatLngBounds가 필요한 패딩을 고려하여 최대한 큰 확대/축소 수준으로 특정 직사각형 내에서 화면 중심에 배치되도록 카메라를 이동합니다.

참고: 지도에서 레이아웃 작업을 한 후 카메라를 이동하는 데 사용하려면 더 간단한 메서드인 newLatLngBounds(boundary, padding)만 사용하여 CameraUpdate를 생성하세요. 레이아웃 작업 중에는 경계 상자를 정확히 투사하는 데 필요한 지도의 표시 경계를 API에서 계산합니다. 그에 비해 API는 사용자가 전달하는 인수에서 표시 경계를 계산하기 때문에 지도가 레이아웃 과정을 거치기 전에 사용자는 언제든지 더 복잡한 메서드 newLatLngBounds(boundary, width, height, padding)에서 반환된 CameraUpdate을 사용할 수 있습니다.

사용자의 패닝을 특정 영역으로 제한

위 시나리오에서는 지도의 경계를 설정했지만 사용자가 이 경계 너머로 스크롤하거나 패닝할 수 있습니다. 그 대신 사용자가 이 경계 안에서만 스크롤하고 패닝할 수 있도록 필요하다면 카메라 초점(카메라 타겟)의 경도/위도 경계를 제한할 수도 있습니다. 예를 들어 쇼핑 센터나 공항의 리테일 앱은 필요에 따라 지도를 특정 경계로 제한함으로써 사용자가 이 경계 안에서만 스크롤하고 패닝하도록 허용할 수 있습니다.

자바

// Create a LatLngBounds that includes the city of Adelaide in Australia.
LatLngBounds adelaideBounds = new LatLngBounds(
    new LatLng(-35.0, 138.58), // SW bounds
    new LatLng(-34.9, 138.61)  // NE bounds
);

// Constrain the camera target to the Adelaide bounds.
map.setLatLngBoundsForCameraTarget(adelaideBounds);
      

Kotlin

// Create a LatLngBounds that includes the city of Adelaide in Australia.
val adelaideBounds = LatLngBounds(
    LatLng(-35.0, 138.58),  // SW bounds
    LatLng(-34.9, 138.61) // NE bounds
)

// Constrain the camera target to the Adelaide bounds.
map.setLatLngBoundsForCameraTarget(adelaideBounds)
      

다음 다이어그램은 카메라 타겟을 표시 영역보다 약간 큰 영역으로 제한한 시나리오를 보여줍니다. 카메라 타겟이 제한된 영역 내에 유지되는 경우 사용자는 스크롤과 패닝을 할 수 있습니다. 십자가는 카메라 타겟을 나타냅니다.

표시 영역보다 큰 카메라 LatLngBounds를
      보여주는 다이어그램

지도에서는 표시 영역을 보여주는 곳이 지정된 경계 밖에 있더라도 항상 표시 영역을 채웁니다. 예를 들어 카메라 타겟을 경계 영역의 모서리에 배치하면 모서리를 벗어난 영역은 표시 영역 내에 표시되지만 사용자는 그 영역 안쪽으로 스크롤할 수 없습니다. 다음 다이어그램은 이 시나리오를 보여줍니다. 십자가는 카메라 타겟을 나타냅니다.

카메라 LatLngBounds의 오른쪽 하단 모서리에 위치한 카메라 타겟을
      보여주는 다이어그램

다음 다이어그램에서 카메라 타겟은 경계가 매우 제한적이므로 사용자가 지도를 스크롤하거나 패닝할 수 있는 기회는 거의 없습니다. 십자가는 카메라 타겟을 나타냅니다.

표시 영역보다 작은 카메라 LatLngBounds를
      보여주는 다이어그램

카메라 뷰 업데이트하기

CameraUpdate를 지도에 적용하기 위해 카메라를 즉시 이동하거나 부드럽게 애니메이션으로 표시할 수 있습니다. 지정된 CameraUpdate로 카메라를 즉시 이동하려면 GoogleMap.moveCamera(CameraUpdate)를 호출하면 됩니다.

특히 짧은 이동 시 변경사항을 애니메이션으로 표시하면 더욱 만족스러운 사용자 경험을 제공할 수 있습니다. 이렇게 하려면 GoogleMap.moveCamera를 호출하는 대신 GoogleMap.animateCamera를 호출합니다. 지도가 새 속성으로 매끄럽게 이동합니다. 이 메서드의 가장 상세한 형식인 GoogleMap.animateCamera(cameraUpdate, duration, callback)는 다음과 같이 세 가지 인수를 제공합니다.

cameraUpdate
CameraUpdate는 카메라를 이동할 위치를 설명합니다.
callback
GoogleMap.CancellableCallback을 구현하는 객체입니다. 작업을 처리하기 위해 이 일반화된 인터페이스는 두 가지 메서드인 `onCancel()`과 `onFinished()`를 정의합니다. 애니메이션의 경우 메서드는 다음과 같은 경우에 호출됩니다.
onFinish()
애니메이션이 중단 없이 완료되면 호출됩니다.
onCancel()

stopAnimation()을 호출하거나 새 카메라 이동을 시작하여 애니메이션이 중단되면 호출됩니다.

또는 GoogleMap.stopAnimation()을 호출하는 경우에도 이 메서드가 호출될 수 있습니다.

duration
원하는 애니메이션 재생 시간(단위: 밀리초)이며 int입니다.

다음 코드 스니펫은 카메라를 이동하는 일반적인 방법을 몇 가지 보여줍니다.

자바

LatLng sydney = new LatLng(-33.88,151.21);
LatLng mountainView = new LatLng(37.4, -122.1);

// Move the camera instantly to Sydney with a zoom of 15.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 15));

// Zoom in, animating the camera.
map.animateCamera(CameraUpdateFactory.zoomIn());

// Zoom out to zoom level 10, animating with a duration of 2 seconds.
map.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);

// Construct a CameraPosition focusing on Mountain View and animate the camera to that position.
CameraPosition cameraPosition = new CameraPosition.Builder()
    .target(mountainView )      // Sets the center of the map to Mountain View
    .zoom(17)                   // Sets the zoom
    .bearing(90)                // Sets the orientation of the camera to east
    .tilt(30)                   // Sets the tilt of the camera to 30 degrees
    .build();                   // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
      

Kotlin

val sydney = LatLng(-33.88, 151.21)
val mountainView = LatLng(37.4, -122.1)

// Move the camera instantly to Sydney with a zoom of 15.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 15f))

// Zoom in, animating the camera.
map.animateCamera(CameraUpdateFactory.zoomIn())

// Zoom out to zoom level 10, animating with a duration of 2 seconds.
map.animateCamera(CameraUpdateFactory.zoomTo(10f), 2000, null)

// Construct a CameraPosition focusing on Mountain View and animate the camera to that position.
val cameraPosition = CameraPosition.Builder()
    .target(mountainView) // Sets the center of the map to Mountain View
    .zoom(17f)            // Sets the zoom
    .bearing(90f)         // Sets the orientation of the camera to east
    .tilt(30f)            // Sets the tilt of the camera to 30 degrees
    .build()              // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))