소개
오버레이는 위도/경도 좌표에 연결되어 지도를 드래그하거나 확대/축소할 때 이동하는 지도상의 객체입니다. 사전 정의된 오버레이 유형에 대한 자세한 내용은 지도에 그리기를 참고하세요.
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>