마커 · 클러스터 · 정량 원형 · 히트맵 · 코로플레스 · 폴리라인 · 코드 그대로 복사 → 데이터만 교체
<head>에 한 번)<div id="map" style="height:440px"> ← 높이 필수!fetch()로 GeoJSON·CSV 로드L.map() → L.tileLayer() → L.marker()/L.heatLayer()/...L.map(id) · 지도 컨테이너 생성. 처음 한 번.L.tileLayer(url) · 베이스맵(OSM·CartoDB·위성). 지도 배경.L.marker([lat, lng]) · 핀 마커 한 개.L.circleMarker([lat, lng], { radius }) · 정량 표현용 원형 마커.L.geoJSON(geo) · GeoJSON으로 폴리곤(행정구역) 그리기.<!-- ① CDN: Leaflet CSS + JS --> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <!-- ② 컨테이너 (높이 필수!) --> <div id="map" style="height:440px"></div> <script> // ③ 데이터 준비 — A안: 직접 작성 const places = [ { lat: 37.5665, lng: 126.978, name: '서울시청', count: 45 }, { lat: 37.5172, lng: 127.0473, name: '강남구청', count: 78 } ]; // ③ 데이터 준비 — B안: 외부 JSON에서 fetch (실무 패턴) // fetch('places.json') // .then(r => r.json()) // .then(places => drawMap(places)); // ④ 지도 생성 + 레이어 const map = L.map('map').setView([37.5665, 126.978], 11); // 중심좌표 + 줌(1~18) L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap' }).addTo(map); places.forEach(p => { L.marker([p.lat, p.lng]) .bindPopup(`<b>${p.name}</b><br>사고 ${p.count}건`) .addTo(map); }); </script>
places 배열만 자기 데이터로 교체하면 끝. 위/경도(lat, lng)와 표시할 이름·값만 맞추면 어떤 지점이든 그릴 수 있습니다.places 배열의 lat/lng/name/count만 교체. 길이만큼 마커가 자동 생성됨.// ② <div id="map1" style="height:440px"></div> // ③ 데이터 준비 ─── 자기 데이터로 교체 const places = [ { lat: 37.5665, lng: 126.978, name: '서울시청', count: 45 }, { lat: 37.5172, lng: 127.0473, name: '강남구청', count: 78 }, { lat: 37.5796, lng: 126.977, name: '광화문', count: 32 }, { lat: 37.5443, lng: 127.0557, name: '잠실역', count: 61 }, { lat: 37.5326, lng: 126.9901, name: '한남대교', count: 53 } ]; // ④ 지도 생성 (3단계) const map = L.map('map1').setView([37.5665, 126.978], 11); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap' }).addTo(map); places.forEach(p => { L.marker([p.lat, p.lng]) .bindPopup(`<b>${p.name}</b><br>사고 ${p.count}건`) .addTo(map); });
<div id="map">에 반드시 height가 지정돼 있어야 합니다.L.markerClusterGroup()로 그룹 만들고 마커들을 그 그룹에 add → 마지막에 map에 add. 카테고리별로 그룹을 분리하면 토글 가능.// ① CDN 추가: // <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css"/> // <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css"/> // <script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script> // ② <div id="map2" style="height:440px"></div> // ③ 데이터 준비 ─── 자기 데이터로 교체 const data = [ { lat: 37.517, lng: 127.047, type: '차대차' }, { lat: 37.544, lng: 127.055, type: '차대사람' }, { lat: 37.498, lng: 127.027, type: '차량단독' } // ... 수백~수천 건도 OK ]; const typeColors = { 차대차:'#3b82f6', 차대사람:'#ef4444', 차량단독:'#f59e0b' }; // ④ 지도 + 클러스터 (카테고리별로 그룹 분리 → 토글 가능) const map = L.map('map2').setView([37.55, 127.01], 11); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); const clusters = {}; ['차대차', '차대사람', '차량단독'].forEach(t => { clusters[t] = L.markerClusterGroup(); // 카테고리당 그룹 1개 }); data.forEach(d => { L.circleMarker([d.lat, d.lng], { radius: 6, fillColor: typeColors[d.type], color: '#fff', weight: 1, fillOpacity: 0.85 }).bindPopup(d.type).addTo(clusters[d.type]); }); Object.values(clusters).forEach(c => map.addLayer(c)); // 체크박스로 토글 // document.getElementById('cb-차대차').onchange = e => // e.target.checked ? map.addLayer(clusters['차대차']) : map.removeLayer(clusters['차대차']);
radius = √(값) / 상수. 직접 r=값으로 하면 큰 값이 4배·9배 과장.radius: Math.sqrt(d.count) / 1.6 같이 √ 변환. 분모(1.6)는 데이터 범위에 맞춰 조정.// ② <div id="map3" style="height:440px"></div> // ③ 데이터 준비 ─── 위치 + 값 const data = [ { name:'강남역', lat:37.4979, lng:127.0276, count:850 }, { name:'서울역', lat:37.5547, lng:126.9706, count:520 }, { name:'잠실역', lat:37.5133, lng:127.1000, count:680 }, { name:'홍대입구', lat:37.5572, lng:126.9244, count:430 }, { name:'사당역', lat:37.4769, lng:126.9817, count:380 } ]; // ④ 지도 + 원형 마커 (반지름은 √값) const map = L.map('map3').setView([37.55, 127.0], 11); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); data.forEach(d => { L.circleMarker([d.lat, d.lng], { radius: Math.sqrt(d.count) / 1.6, // ← √ 변환 핵심 fillColor: '#ef4444', color: 'white', weight: 2, fillOpacity: 0.65 }).bindPopup(`<b>${d.name}</b><br>사고 ${d.count}건`).addTo(map); });
[[위도, 경도, 강도(0~1)], ...] 3원소 배열들의 배열. 강도는 생략 가능.// ① CDN: leaflet.heat 추가 // <script src="https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js"></script> // ② <div id="map4" style="height:440px"></div> // ③ 데이터 준비 ─── [위도, 경도, 강도] 3원소 배열 const heatPoints = [ [37.517, 127.047, 0.8], [37.518, 127.046, 0.6], [37.544, 127.055, 0.9] // ... 수천 개도 OK. 강도(3번째 값) 생략 시 1로 처리 ]; // ④ 지도 + 히트맵 레이어 const map = L.map('map4').setView([37.55, 127.01], 12); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); L.heatLayer(heatPoints, { radius: 22, // 한 점이 차지하는 반경 blur: 15, // 흐림 정도 maxZoom: 14, gradient: { 0.2: 'blue', 0.5: 'lime', 0.8: 'yellow', 1: 'red' } }).addTo(map);
{시도명: 값}). 둘을 fetch로 가져와 결합.// ② <div id="map5" style="height:440px"></div> // ③ 데이터 준비 ─── 통계 객체 (시도명 → 값) const regionData = { '서울':34215, '경기':58432, '부산':15621, '대구':9843, '인천':14523, '광주':7234, '대전':8123, '울산':5234 // ... 자기 데이터로 }; // 색상 함수 (값 → 색) function getColor(v) { return v > 50000 ? '#084594' : v > 30000 ? '#2171b5' : v > 15000 ? '#4292c6' : v > 10000 ? '#6baed6' : v > 5000 ? '#9ecae1' : '#deebf7'; } // ④ 지도 + GeoJSON (외부 파일 fetch) const map = L.map('map5').setView([36.3, 127.8], 7); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); fetch('https://cdn.jsdelivr.net/gh/southkorea/southkorea-maps@master/kostat/2018/json/skorea-provinces-2018-geo.json') .then(r => r.json()) .then(geo => { L.geoJSON(geo, { style: f => ({ // 각 행정구역의 스타일 fillColor: getColor(regionData[f.properties.name] || 0), fillOpacity: 0.75, color: 'white', weight: 1 }), onEachFeature: (f, lyr) => { // 각 구역에 이벤트 부여 const name = f.properties.name; const v = regionData[name] || 0; lyr.bindPopup(`<b>${name}</b><br>사고 ${v.toLocaleString()}건`); lyr.on('mouseover', () => lyr.setStyle({ fillOpacity:0.95, weight:2.5 })); lyr.on('mouseout', () => lyr.setStyle({ fillOpacity:0.75, weight:1 })); } }).addTo(map); });
cdn.jsdelivr.net/gh/southkorea/southkorea-maps)을 사용. 인터넷 차단 시 자동 사각형 폴백.L.polyline(좌표 배열), L.polygon(닫힌 좌표 배열), L.circle(중심+반경 미터).// ② <div id="map6" style="height:440px"></div> // ③ 데이터 준비 const route = [ [37.5665, 126.978], [37.560, 127.000], [37.553, 127.018], [37.546, 127.040], [37.540, 127.060], [37.535, 127.082] ]; const aoi = [[37.555,126.97],[37.555,127.00],[37.540,127.00],[37.540,126.97]]; const circleCenter = [37.510, 127.05], radiusMeter = 1500; // ④ 지도 + 3가지 도형 const map = L.map('map6').setView([37.535, 127.0], 12); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map); // 폴리라인 (경로) L.polyline(route, { color: '#ef4444', weight: 5, opacity: 0.85 }) .addTo(map).bindPopup('시뮬레이션 경로'); // 폴리곤 (닫힌 영역) L.polygon(aoi, { color: '#3b82f6', fillColor: '#3b82f6', fillOpacity: 0.25 }) .addTo(map).bindPopup('관심 영역'); // 원 (반경, 단위: 미터) L.circle(circleCenter, { radius: radiusMeter, color: '#10b981', fillOpacity: 0.18 }) .addTo(map).bindPopup('반경 1.5km 원'); // 출발/도착 마커 L.marker(route[0]).addTo(map).bindPopup('출발'); L.marker(route.at(-1)).addTo(map).bindPopup('도착');
L.control.layers(베이스, 오버레이).addTo(map) 한 줄. 객체 키가 UI 라벨.// ② <div id="map7" style="height:440px"></div> // ③ 베이스맵 + 오버레이 레이어 정의 const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'); const dark = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png'); const light = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png'); // ④ 지도 + 컨트롤 const map = L.map('map7').setView([37.55, 127.0], 11); osm.addTo(map); // 처음 보일 베이스맵 const markerLayer = L.layerGroup([ L.marker([37.5665, 126.978]).bindPopup('서울시청'), L.marker([37.5172, 127.0473]).bindPopup('강남구청') ]); markerLayer.addTo(map); L.control.layers( { 'OSM (기본)': osm, 'Dark': dark, 'Light': light }, // 베이스 (라디오) { '주요 교차로 마커': markerLayer } // 오버레이 (체크) ).addTo(map);