개요
이 튜토리얼을 통해 Google 지도에서 범례를 만드는 방법을 알아보세요. 범례는 일반적으로 지도 위의 기호와 마커를 설명합니다. 맞춤 컨트롤의 배치 기능을 사용하여 맞춤 컨트롤을 만들 수 있습니다.
아래 지도에는 지도 위의 세 가지 다른 맞춤 마커 에 대한 정보를 제공하는 범례가 포함되어 있습니다.
아래 섹션에서는 범례를 만들기 위한 주요 단계를 설명합니다.
전체 코드는 전체 코드 예시 섹션을 참고하세요.
범례 컨테이너 만들기
gmp-map 요소에 div를 추가하여 범례 컨테이너를 만듭니다.
컨트롤 슬롯팅을 사용하여 범례의 위치를 설정합니다. 컨트롤 배치 옵션에 대해 자세히 알아보세요.
<gmp-map center="-33.91722,151.23064" zoom="16.5" map-id="DEMO_MAP_ID">
<div id="legend" slot="control-inline-start-block-start"></div>
</gmp-map>
스타일을 적용하여 범례의 시각적 모양을 설정합니다. 이 예에서는 범례 컨테이너, 제목, 항목의 스타일을 지정합니다.
#legend {
background-color: #fff;
width: 15em;
margin: 1em;
border-radius: 8px;
font-family: Roboto, Arial, sans-serif;
overflow: hidden;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
.legend-item {
margin: 0.5em 1em;
display: flex;
flex-direction: row;
align-items: center;
padding-bottom: 0.5em;
}
.legend-icon {
margin-right: 1em;
font-size: 24px;
}
.legend-text {
font-size: 1.5em;
}
범례 콘텐츠 추가
makeLegend() 함수는 프로그래매틱 방식으로 시각적 지도 범례를 빌드하고 페이지의 DOM에 삽입합니다. 실행 방식은 다음과 같습니다.
- 라이브러리 가져오기: Maps SDK (
await google.maps.importLibrary('marker'))에서PinElement클래스를 동적으로 가져옵니다.
const { PinElement } = await google.maps.importLibrary('marker');
- 컨테이너 준비: HTML에서 기존의 빈
<div id="legend">를 가져와서 '범례' 제목 요소를 앞에 추가합니다.
const legend = document.getElementById('legend')!;
const title = document.createElement('div');
title.innerText = 'Legend';
title.classList.add('title');
legend.appendChild(title);
- 구성 반복:
icons구성 객체를 반복하여 각 항목 (주차, 도서관, 정보)의 행을 생성합니다. 도우미 함수는 다음과 같습니다.
async function makeLegend() { const { PinElement } = await google.maps.importLibrary('marker'); const legend = document.getElementById('legend'); const title = document.createElement('div'); title.innerText = 'Legend'; title.classList.add('title'); legend.appendChild(title); for (const type of Object.values(icons)) { const wrapper = document.createElement('div'); wrapper.classList.add('legend-item'); const pin = new PinElement({ background: type.color, borderColor: type.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}` ), scale: 1.0, }); // PinElement extends HTMLElement directly, so we can append it directly! const pinContainer = document.createElement('div'); pinContainer.classList.add('legend-icon'); pinContainer.appendChild(pin); const txt = document.createElement('div'); txt.classList.add('legend-text'); txt.innerText = type.name; wrapper.appendChild(pinContainer); wrapper.appendChild(txt); legend.appendChild(wrapper); } }
프로그래매틱 방식으로 생성된 범례의 또 다른 예는 포인트 데이터 기능 스타일 지정 을 참고하세요.
전체 코드 예시
아래 섹션에는 이 튜토리얼에서 지도와 범례를 만드는 데 필요한 전체 코드가 표시되어 있습니다.
TypeScript
const parkingSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 3H6v18h4v-6h3c3.31 0 6-2.69 6-6s-2.69-6-6-6zm.2 8H10V7h3.2c1.1 0 2 .9 2 2s-.9 2-2 2z"/></svg>`; const librarySvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></svg>`; const infoSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>`; const icons: Record<string, { name: string; icon: string; color: string }> = { parking: { name: 'Parking', icon: parkingSvg, color: '#1E88E5', // Blue }, library: { name: 'Library', icon: librarySvg, color: '#43A047', // Green }, info: { name: 'Info', icon: infoSvg, color: '#E53935', // Red }, }; async function init(): Promise<void> { const [{ AdvancedMarkerElement, PinElement }, { event }] = await Promise.all([ google.maps.importLibrary('marker'), google.maps.importLibrary('core'), google.maps.importLibrary('maps'), ]); const mapElement = document.querySelector('gmp-map')!; const innerMap = mapElement.innerMap; // Wait for the map to load before building the legend. event.addListenerOnce(innerMap, 'idle', () => { void makeLegend(); }); const features = [ { position: { lat: -33.91721, lng: 151.2263 }, type: 'info', }, { position: { lat: -33.91539, lng: 151.2282 }, type: 'info', }, { position: { lat: -33.91747, lng: 151.22912 }, type: 'info', }, { position: { lat: -33.9191, lng: 151.22907 }, type: 'info', }, { position: { lat: -33.91725, lng: 151.23011 }, type: 'info', }, { position: { lat: -33.91872, lng: 151.23089 }, type: 'info', }, { position: { lat: -33.91784, lng: 151.23094 }, type: 'info', }, { position: { lat: -33.91682, lng: 151.23149 }, type: 'info', }, { position: { lat: -33.9179, lng: 151.23463 }, type: 'info', }, { position: { lat: -33.91666, lng: 151.23468 }, type: 'info', }, { position: { lat: -33.916988, lng: 151.23364 }, type: 'info', }, { position: { lat: -33.91662347903106, lng: 151.22879464019775 }, type: 'parking', }, { position: { lat: -33.916365282092855, lng: 151.22937399734496 }, type: 'parking', }, { position: { lat: -33.91665018901448, lng: 151.2282474695587 }, type: 'parking', }, { position: { lat: -33.919543720969806, lng: 151.23112279762267 }, type: 'parking', }, { position: { lat: -33.91608037421864, lng: 151.23288232673644 }, type: 'parking', }, { position: { lat: -33.91851096391805, lng: 151.2344058214569 }, type: 'parking', }, { position: { lat: -33.91818154739766, lng: 151.2346203981781 }, type: 'parking', }, { position: { lat: -33.91727341958453, lng: 151.23348314155578 }, type: 'library', }, ]; for (const feature of features) { const iconData = icons[feature.type]; const pin = new PinElement({ background: iconData.color, borderColor: iconData.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(iconData.icon)}` ), scale: 1.5, }); const marker = new AdvancedMarkerElement({ position: feature.position, title: iconData.name, }); marker.append(pin); mapElement.append(marker); } } async function makeLegend() { const { PinElement } = await google.maps.importLibrary('marker'); const legend = document.getElementById('legend')!; const title = document.createElement('div'); title.innerText = 'Legend'; title.classList.add('title'); legend.appendChild(title); for (const type of Object.values(icons)) { const wrapper = document.createElement('div'); wrapper.classList.add('legend-item'); const pin = new PinElement({ background: type.color, borderColor: type.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}` ), scale: 1.0, }); // PinElement extends HTMLElement directly, so we can append it directly! const pinContainer = document.createElement('div'); pinContainer.classList.add('legend-icon'); pinContainer.appendChild(pin); const txt = document.createElement('div'); txt.classList.add('legend-text'); txt.innerText = type.name; wrapper.appendChild(pinContainer); wrapper.appendChild(txt); legend.appendChild(wrapper); } } void init();
JavaScript
const parkingSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 3H6v18h4v-6h3c3.31 0 6-2.69 6-6s-2.69-6-6-6zm.2 8H10V7h3.2c1.1 0 2 .9 2 2s-.9 2-2 2z"/></svg>`; const librarySvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></svg>`; const infoSvg = `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>`; const icons = { parking: { name: 'Parking', icon: parkingSvg, color: '#1E88E5', // Blue }, library: { name: 'Library', icon: librarySvg, color: '#43A047', // Green }, info: { name: 'Info', icon: infoSvg, color: '#E53935', // Red }, }; async function init() { const [{ AdvancedMarkerElement, PinElement }, { event }] = await Promise.all([ google.maps.importLibrary('marker'), google.maps.importLibrary('core'), google.maps.importLibrary('maps'), ]); const mapElement = document.querySelector('gmp-map'); const innerMap = mapElement.innerMap; // Wait for the map to load before building the legend. event.addListenerOnce(innerMap, 'idle', () => { void makeLegend(); }); const features = [ { position: { lat: -33.91721, lng: 151.2263 }, type: 'info', }, { position: { lat: -33.91539, lng: 151.2282 }, type: 'info', }, { position: { lat: -33.91747, lng: 151.22912 }, type: 'info', }, { position: { lat: -33.9191, lng: 151.22907 }, type: 'info', }, { position: { lat: -33.91725, lng: 151.23011 }, type: 'info', }, { position: { lat: -33.91872, lng: 151.23089 }, type: 'info', }, { position: { lat: -33.91784, lng: 151.23094 }, type: 'info', }, { position: { lat: -33.91682, lng: 151.23149 }, type: 'info', }, { position: { lat: -33.9179, lng: 151.23463 }, type: 'info', }, { position: { lat: -33.91666, lng: 151.23468 }, type: 'info', }, { position: { lat: -33.916988, lng: 151.23364 }, type: 'info', }, { position: { lat: -33.91662347903106, lng: 151.22879464019775 }, type: 'parking', }, { position: { lat: -33.916365282092855, lng: 151.22937399734496 }, type: 'parking', }, { position: { lat: -33.91665018901448, lng: 151.2282474695587 }, type: 'parking', }, { position: { lat: -33.919543720969806, lng: 151.23112279762267 }, type: 'parking', }, { position: { lat: -33.91608037421864, lng: 151.23288232673644 }, type: 'parking', }, { position: { lat: -33.91851096391805, lng: 151.2344058214569 }, type: 'parking', }, { position: { lat: -33.91818154739766, lng: 151.2346203981781 }, type: 'parking', }, { position: { lat: -33.91727341958453, lng: 151.23348314155578 }, type: 'library', }, ]; for (const feature of features) { const iconData = icons[feature.type]; const pin = new PinElement({ background: iconData.color, borderColor: iconData.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(iconData.icon)}` ), scale: 1.5, }); const marker = new AdvancedMarkerElement({ position: feature.position, title: iconData.name, }); marker.append(pin); mapElement.append(marker); } } async function makeLegend() { const { PinElement } = await google.maps.importLibrary('marker'); const legend = document.getElementById('legend'); const title = document.createElement('div'); title.innerText = 'Legend'; title.classList.add('title'); legend.appendChild(title); for (const type of Object.values(icons)) { const wrapper = document.createElement('div'); wrapper.classList.add('legend-item'); const pin = new PinElement({ background: type.color, borderColor: type.color, glyphSrc: new URL( `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(type.icon)}` ), scale: 1.0, }); // PinElement extends HTMLElement directly, so we can append it directly! const pinContainer = document.createElement('div'); pinContainer.classList.add('legend-icon'); pinContainer.appendChild(pin); const txt = document.createElement('div'); txt.classList.add('legend-text'); txt.innerText = type.name; wrapper.appendChild(pinContainer); wrapper.appendChild(txt); legend.appendChild(wrapper); } } void init();
CSS
html, body { height: 100%; margin: 0; padding: 0; } #legend { background-color: #fff; width: 15em; margin: 1em; border-radius: 8px; font-family: Roboto, Arial, sans-serif; overflow: hidden; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3); } .title { padding: 0.5em 1em; font-weight: bold; font-size: 1.5em; margin-bottom: 0.5em; background-color: rgb(66, 133, 244); color: white; box-sizing: border-box; } .legend-item { margin: 0.5em 1em; display: flex; flex-direction: row; align-items: center; padding-bottom: 0.5em; } .legend-icon { margin-right: 1em; font-size: 24px; } .legend-text { font-size: 1.5em; }
HTML
<html>
<head>
<title>Custom Legend</title>
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script>
<script>
// prettier-ignore
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: "GOOGLE_MAPS_API_KEY"
});
</script>
</head>
<body>
<gmp-map center="-33.91722,151.23064" zoom="16.5" map-id="DEMO_MAP_ID">
<div id="legend" slot="control-inline-start-block-start"></div>
</gmp-map>
</body>
</html>