소개
오버레이는 위도/경도 좌표에 연결되어 지도를 드래그하거나 확대/축소할 때 이동하는 지도상의 객체입니다. 사전 정의된 오버레이 유형에 대한 자세한 내용은 지도에 그리기를 참고하세요.
Maps JavaScript API는 맞춤 오버레이를 만들 수 있는 OverlayView 클래스를 제공합니다. OverlayView는 오버레이를 만들 때 구현해야 하는 여러 메서드를 제공하는 기본 클래스로서, 화면 좌표와 지도상의 위치 간에 서로 변환할 수 있도록 지원하는 메서드도 제공합니다.
맞춤 오버레이 추가
다음은 맞춤 오버레이를 만드는 데 필요한 단계를 요약한 것입니다.
- 맞춤 오버레이 객체의 prototype을 새google.maps.OverlayView()인스턴스로 설정합니다. 이렇게 하면 오버레이 클래스의 서브클래스가 생성됩니다.
- 맞춤 오버레이의 생성자를 만들고 초기화 매개변수를 설정합니다.
- 프로토타입 내에서 onAdd()메서드를 구현하고 오버레이를 지도에 연결합니다. 지도에 오버레이를 연결할 준비가 되면OverlayView.onAdd()가 호출됩니다.
- 프로토타입 내에서 draw()메서드를 구현하고 객체의 시각적 표시를 처리합니다. 객체가 처음 표시될 때OverlayView.draw()가 호출됩니다.
- 또한 onRemove()메서드를 구현하여 오버레이 내에 추가한 요소를 정리해야 합니다.
아래는 각 단계에 대한 자세한 설명입니다. 실제로 작동하는 전체 예시 코드를 확인할 수 있습니다. 예시 코드 보기
오버레이 서브클래스 생성
아래 예에서는 OverlayView를 사용하여 간단한 이미지 오버레이를 만듭니다.
이제 USGSOverlay 클래스의 생성자를 만들고 전달된 매개변수를 새 객체의 속성으로 초기화합니다.
TypeScript
/** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { private bounds: google.maps.LatLngBounds; private image: string; private div?: HTMLElement; constructor(bounds: google.maps.LatLngBounds, image: string) { super(); this.bounds = bounds; this.image = image; }
JavaScript
/** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { bounds; image; div; constructor(bounds, image) { super(); this.bounds = bounds; this.image = image; }
아직 오버레이의 생성자에서 이 오버레이를 지도에 연결할 수 없습니다. 지도의 창은 객체가 지도에 표시되는 순서를 지정하므로 먼저 지도의 모든 창이 사용 가능한지 확인해야 합니다. API는 이와 같은 경우가 발생했음을 알려주는 지원 메서드를 제공합니다. 다음 섹션에서 이 메서드에 대해 설명합니다.
오버레이 초기화
오버레이가 처음 인스턴스화되고 표시할 준비가 되면, 브라우저의 DOM을 통해 지도에 연결해야 합니다. API는 오버레이의 onAdd() 메서드를 호출하여 오버레이가 지도에 추가되었음을 나타냅니다. 이 메서드를 처리하기 위해 이미지를 보관할 <div>를 만들고 <img> 요소를 추가해 <div>에 연결한 다음 오버레이를 지도의 창 하나에 연결합니다. 창은 DOM 트리 내의 노드입니다.
MapPanes 유형의 창에서는 지도에 여러 레이어를 쌓는 순서를 지정합니다. 다음과 같은 창을 사용할 수 있으며 아래에서 위로 쌓이는 순서대로 열거되어 있습니다.
- mapPane은 가장 아래에 있는 창으로 타일 위에 있으며, DOM 이벤트를 수신하지 않을 수도 있습니다. (창 0)
- overlayLayer에는 다중선, 다각형, 지면 오버레이, 타일 레이어 오버레이가 포함되며, DOM 이벤트를 수신하지 않을 수도 있습니다. (창 1)
- markerLayer에는 마커가 포함되며, DOM 이벤트를 수신하지 않을 수도 있습니다. (창 2)
- overlayMouseTarget에는 DOM 이벤트를 수신하는 요소가 포함됩니다(창 3).
- floatPane에는 정보 창이 포함되며 모든 지도 오버레이 위에 위치합니다. (창 4)
이미지는 '지면 오버레이'이므로 overlayLayer 창이 사용됩니다. 이 창이 있으면 객체를 창에 하위 요소로 연결합니다.
TypeScript
/** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes()!; panes.overlayLayer.appendChild(this.div); }
JavaScript
/** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes(); panes.overlayLayer.appendChild(this.div); }
오버레이 그리기
위 코드에서는 특별한 시각적 표시를 호출하지 않았습니다. API는 처음 추가할 때를 포함하여 지도에 오버레이를 그려야 할 때마다 오버레이에서 별도의 draw() 메서드를 호출합니다.
따라서 이 draw() 메서드를 구현하고 getProjection()을 사용하여 오버레이의 MapCanvasProjection을 가져오고 객체의 오른쪽 상단과 왼쪽 하단 지점을 고정할 정확한 좌표를 계산합니다.
그런 다음 <div>의 크기를 조절할 수 있습니다. 이렇게 하면 오버레이의 생성자에서 지정한 경계와 일치하도록 이미지 크기가 조절됩니다.
TypeScript
draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest() )!; const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast() )!; // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } }
JavaScript
draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest(), ); const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast(), ); // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } }
맞춤 오버레이 제거
onRemove() 메서드를 추가하여 오버레이를 지도에서 완전히 삭제할 수도 있습니다.
TypeScript
/** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { (this.div.parentNode as HTMLElement).removeChild(this.div); delete this.div; } }
JavaScript
/** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { this.div.parentNode.removeChild(this.div); delete this.div; } }
맞춤 오버레이 숨기기 및 표시하기
오버레이를 단순히 만들거나 삭제하는 대신 숨기거나 표시하려면 자체적으로 hide() 및 show() 메서드를 구현하여 오버레이 표시 여부를 조정할 수 있습니다. 또는 오버레이를 지도의 DOM에서 분리할 수 있습니다. 다만 이 작업은 약간 더 많은 비용이 듭니다. 그런 다음 오버레이를 지도의 DOM에 다시 연결하면 오버레이의 onAdd() 메서드가 다시 호출됩니다.
다음 예에서는 hide()와 show() 메서드를 오버레이의 프로토타입에 추가하여 컨테이너 <div>의 표시 여부를 전환합니다. 또한 toggleDOM() 메서드를 추가하여 지도에 오버레이를 연결하거나 지도에서 분리합니다.
TypeScript
/** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map: google.maps.Map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } }
JavaScript
/** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } }
버튼 컨트롤 추가
toggle 및 toggleDom 메서드를 실행하기 위해 버튼 컨트롤이 지도에 추가됩니다.
TypeScript
const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton);
JavaScript
const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton);
전체 샘플 코드
다음은 전체 샘플 코드입니다.
TypeScript
// This example adds hide() and show() methods to a custom overlay's prototype. // These methods toggle the visibility of the container <div>. // overlay to or from the map. function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 11, center: { lat: 62.323907, lng: -150.109291 }, mapTypeId: "satellite", } ); const bounds = new google.maps.LatLngBounds( new google.maps.LatLng(62.281819, -150.287132), new google.maps.LatLng(62.400471, -150.005608) ); // The photograph is courtesy of the U.S. Geological Survey. let image = "https://developers.google.com/maps/documentation/javascript/"; image += "examples/full/images/talkeetna.png"; /** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { private bounds: google.maps.LatLngBounds; private image: string; private div?: HTMLElement; constructor(bounds: google.maps.LatLngBounds, image: string) { super(); this.bounds = bounds; this.image = image; } /** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes()!; panes.overlayLayer.appendChild(this.div); } draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest() )!; const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast() )!; // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } } /** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { (this.div.parentNode as HTMLElement).removeChild(this.div); delete this.div; } } /** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map: google.maps.Map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } } } const overlay: USGSOverlay = new USGSOverlay(bounds, image); overlay.setMap(map); const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton); } declare global { interface Window { initMap: () => void; } } window.initMap = initMap;
JavaScript
// This example adds hide() and show() methods to a custom overlay's prototype. // These methods toggle the visibility of the container <div>. // overlay to or from the map. function initMap() { const map = new google.maps.Map(document.getElementById("map"), { zoom: 11, center: { lat: 62.323907, lng: -150.109291 }, mapTypeId: "satellite", }); const bounds = new google.maps.LatLngBounds( new google.maps.LatLng(62.281819, -150.287132), new google.maps.LatLng(62.400471, -150.005608), ); // The photograph is courtesy of the U.S. Geological Survey. let image = "https://developers.google.com/maps/documentation/javascript/"; image += "examples/full/images/talkeetna.png"; /** * The custom USGSOverlay object contains the USGS image, * the bounds of the image, and a reference to the map. */ class USGSOverlay extends google.maps.OverlayView { bounds; image; div; constructor(bounds, image) { super(); this.bounds = bounds; this.image = image; } /** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ onAdd() { this.div = document.createElement("div"); this.div.style.borderStyle = "none"; this.div.style.borderWidth = "0px"; this.div.style.position = "absolute"; // Create the img element and attach it to the div. const img = document.createElement("img"); img.src = this.image; img.style.width = "100%"; img.style.height = "100%"; img.style.position = "absolute"; this.div.appendChild(img); // Add the element to the "overlayLayer" pane. const panes = this.getPanes(); panes.overlayLayer.appendChild(this.div); } draw() { // We use the south-west and north-east // coordinates of the overlay to peg it to the correct position and size. // To do this, we need to retrieve the projection from the overlay. const overlayProjection = this.getProjection(); // Retrieve the south-west and north-east coordinates of this overlay // in LatLngs and convert them to pixel coordinates. // We'll use these coordinates to resize the div. const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest(), ); const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast(), ); // Resize the image's div to fit the indicated dimensions. if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; } } /** * The onRemove() method will be called automatically from the API if * we ever set the overlay's map property to 'null'. */ onRemove() { if (this.div) { this.div.parentNode.removeChild(this.div); delete this.div; } } /** * Set the visibility to 'hidden' or 'visible'. */ hide() { if (this.div) { this.div.style.visibility = "hidden"; } } show() { if (this.div) { this.div.style.visibility = "visible"; } } toggle() { if (this.div) { if (this.div.style.visibility === "hidden") { this.show(); } else { this.hide(); } } } toggleDOM(map) { if (this.getMap()) { this.setMap(null); } else { this.setMap(map); } } } const overlay = new USGSOverlay(bounds, image); overlay.setMap(map); const toggleButton = document.createElement("button"); toggleButton.textContent = "Toggle"; toggleButton.classList.add("custom-map-control-button"); const toggleDOMButton = document.createElement("button"); toggleDOMButton.textContent = "Toggle DOM Attachment"; toggleDOMButton.classList.add("custom-map-control-button"); toggleButton.addEventListener("click", () => { overlay.toggle(); }); toggleDOMButton.addEventListener("click", () => { overlay.toggleDOM(map); }); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleDOMButton); map.controls[google.maps.ControlPosition.TOP_RIGHT].push(toggleButton); } window.initMap = initMap;
CSS
/* * Always set the map height explicitly to define the size of the div element * that contains the map. */ #map { height: 100%; } /* * Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } .custom-map-control-button { background-color: #fff; border: 0; border-radius: 2px; box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3); margin: 10px; padding: 0 0.5em; font: 400 18px Roboto, Arial, sans-serif; overflow: hidden; height: 40px; cursor: pointer; } .custom-map-control-button:hover { background: rgb(235, 235, 235); }
HTML
<html>
  <head>
    <title>Showing/Hiding Overlays</title>
    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script type="module" src="./index.js"></script>
  </head>
  <body>
    <div id="map"></div>
    <!-- 
      The `defer` attribute causes the script to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises. See
      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>