آموزش جامع Leaflet.js
کتابخانه نقشه تعاملی سبک و قدرتمند — رایگان، آفلاینپذیر و بدون API key.
📚معرفی و نصب
Leaflet.js یکی از محبوبترین کتابخانههای جاوااسکریپت برای نمایش نقشههای تعاملی در مرورگر است. برخلاف Google Maps که نیازمند API key است، Leaflet با منابع باز مثل OpenStreetMap کار میکند.
مقایسه با Google Maps
| ویژگی | Leaflet | Google Maps |
|---|---|---|
| قیمت | رایگان کامل | پولی بعد از سهمیه |
| حریم خصوصی | بدون tracking | Google tracking |
| حجم | ~42KB | ~300KB+ |
| سفارشیسازی | بسیار بالا | محدود |
| Tile Sources | هر منبعی | فقط Google |
| API Key | نیاز ندارد | اجباری |
نصب
<!-- ۱. CSS حتماً قبل از JS --> <link rel="stylesheet" href="leaflet.min.css"> <!-- ۲. JS --> <script src="leaflet.min.js"></script>
ساختار اولیه
<!-- div با ارتفاع مشخص -- اجباری! --> <div id="map" style="height: 400px;"></div> <script> // ایجاد نقشه روی تهران const map = L.map('map').setView([35.6892, 51.3890], 13); // افزودن لایه tile از OpenStreetMap L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); </script>
height: 400px — وگرنه نقشه نمایش داده نمیشود!
🌎نقشه پایه
مختصات و سطح Zoom
// setView([latitude, longitude], zoomLevel) const map = L.map('map').setView([35.6892, 51.3890], 13); // تهران: lat=35.6892, lng=51.3890 // zoom: 1 (کل جهان) تا 19 (ساختمان)
| Zoom | نمایش |
|---|---|
| 1 – 3 | کل جهان / قارهها |
| 4 – 6 | کشورها |
| 7 – 10 | استانها و شهرها |
| 11 – 14 | بخشهای شهر |
| 15 – 17 | خیابانها |
| 18 – 19 | ساختمانها |
Tile Layerهای رایج
// OpenStreetMap (رایگان) L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', maxZoom: 19 }).addTo(map); // OpenTopoMap — نقشه توپوگرافی (رایگان) L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', { attribution: '© OpenTopoMap', maxZoom: 17 }).addTo(map); // CartoDB Dark (رایگان) L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { attribution: '© CartoDB' }).addTo(map);
📍Markers و Popups
Marker پایه
// اضافه کردن marker const marker = L.marker([35.6892, 51.3890]).addTo(map); // Popup به marker marker.bindPopup('<b>تهران</b><br>پایتخت ایران').openPopup(); // یا chain style L.marker([35.6892, 51.3890]) .addTo(map) .bindPopup('<b>تهران</b>') .openPopup(); // Popup مستقل (بدون marker) L.popup() .setLatLng([35.6892, 51.3890]) .setContent('سلام از تهران!') .openOn(map); // Tooltip (نمایش دائم) marker.bindTooltip('تهران', { permanent: true });
Custom Icon
const myIcon = L.icon({ iconUrl: 'marker-icon.png', iconSize: [38, 95], // عرض، ارتفاع iconAnchor: [19, 95], // نقطه اتصال (پایین وسط) popupAnchor: [0, -95] // موقعیت popup }); L.marker([35.6892, 51.3890], { icon: myIcon }).addTo(map); // DivIcon — آیکون با HTML/CSS دلخواه const divIcon = L.divIcon({ html: '<div style="background:#e74c3c;color:white;padding:4px 8px;border-radius:4px">📍</div>', iconSize: [30, 30], className: '' });
روی هر marker کلیک کنید تا اطلاعات شهر نمایش داده شود.
🔷اشکال هندسی
// دایره (Circle) L.circle([35.6892, 51.3890], { radius: 5000, // شعاع به متر color: '#e74c3c', fillColor: '#e74c3c', fillOpacity: 0.3 }).addTo(map).bindPopup('دایره ۵ کیلومتری'); // Polyline — خط شکسته L.polyline([ [35.6892, 51.3890], // تهران [35.7200, 51.4000], // شمال [35.7448, 51.3753] // شمالغرب ], { color: '#3498db', weight: 3, dashArray: '8,4' }).addTo(map); // Polygon — چندضلعی L.polygon([ [35.7, 51.3], [35.7, 51.5], [35.6, 51.5], [35.6, 51.3] ], { color: '#27ae60', fillOpacity: 0.25 }).addTo(map); // Rectangle — مستطیل L.rectangle([[35.6, 51.3], [35.8, 51.6]], { color: '#9b59b6', fillOpacity: 0.15 }).addTo(map); // CircleMarker — دایره با اندازه ثابت (px نه متر) L.circleMarker([35.6892, 51.3890], { radius: 10, color: '#e67e22', fillOpacity: 0.8 }).addTo(map);
⚡Events و تعامل
رویدادهای نقشه
// کلیک روی نقشه map.on('click', function(e) { const lat = e.latlng.lat.toFixed(6); const lng = e.latlng.lng.toFixed(6); L.popup().setLatLng(e.latlng) .setContent(`📍 ${lat}, ${lng}`) .openOn(map); }); // تغییر zoom map.on('zoomend', function() { console.log('Zoom:', map.getZoom()); }); // حرکت نقشه map.on('moveend', function() { const c = map.getCenter(); console.log('مرکز:', c.lat, c.lng); }); // رویداد روی Marker marker.on('click', function(e) { console.log('marker کلیک شد'); }); marker.on('mouseover', function() { this.openPopup(); }); marker.on('mouseout', function() { this.closePopup(); });
📚Layers و Controls
LayerGroup و FeatureGroup
// LayerGroup — گروهبندی چند لایه const cityLayer = L.layerGroup([ L.marker([35.6892, 51.3890]).bindPopup('تهران'), L.marker([32.6539, 51.6660]).bindPopup('اصفهان') ]).addTo(map); // FeatureGroup — عملیات مشترک روی همه const group = L.featureGroup([ L.marker([35.6892, 51.3890]), L.circle([35.6892, 51.3890], { radius: 3000 }) ]).addTo(map); // zoom به کل group map.fitBounds(group.getBounds());
Layer Control و Scale
const streets = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'); const topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png'); // کنترل سوئیچ لایهها L.control.layers({ '🗺️ نقشه خیابانی': streets, '🏔️ توپوگرافی': topo }).addTo(map); // Scale control L.control.scale({ imperial: false }).addTo(map);
از کنترل گوشه بالاراست برای سوئیچ بین نقشه خیابانی و توپوگرافی استفاده کنید.
📄GeoJSON
GeoJSON یک فرمت استاندارد JSON برای دادههای جغرافیایی است. Leaflet از آن بهصورت بومی پشتیبانی میکند.
[longitude, latitude] است اما در Leaflet [latitude, longitude] است. دقت کنید!
const geojsonData = { type: "FeatureCollection", features: [ { type: "Feature", properties: { name: "تهران", population: 15000000 }, geometry: { type: "Point", coordinates: [51.3890, 35.6892] // [lng, lat] } }, { type: "Feature", properties: { name: "ناحیه مرکزی" }, geometry: { type: "Polygon", coordinates: [[[50,34],[56,34],[56,38],[50,38],[50,34]]] } } ] }; L.geoJSON(geojsonData, { // popup برای هر feature onEachFeature: function(feature, layer) { if (feature.properties.name) layer.bindPopup(feature.properties.name); }, // تبدیل Point به CircleMarker pointToLayer: function(feature, latlng) { return L.circleMarker(latlng, { radius: 10, fillColor: '#e74c3c', fillOpacity: 0.8 }); }, // استایل Polygon style: function(feature) { if (feature.geometry.type === 'Polygon') return { color: '#3498db', fillOpacity: 0.1 }; } }).addTo(map);
پلاگینهای محبوب
| پلاگین | کاربرد |
|---|---|
| Leaflet.markercluster | گروهبندی marker در zoom پایین |
| Leaflet.heat | نقشه حرارتی (heatmap) |
| Leaflet-Routing-Machine | مسیریابی بین نقاط |
| Leaflet.draw | رسم اشکال روی نقشه |
| Leaflet.Search | جستجوی مکان |
| Leaflet.fullscreen | تمامصفحه کردن نقشه |
⚙متدهای مفید
متدهای نقشه
// دریافت اطلاعات فعلی map.getCenter() // مرکز نقشه — {lat, lng} map.getZoom() // سطح zoom فعلی map.getBounds() // محدوده نمایش — LatLngBounds // تغییر نما map.setView([35.69, 51.39], 13) map.panTo([35.69, 51.39]) // حرکت بدون تغییر zoom map.flyTo([35.69, 51.39], 13) // انیمیشن پرواز map.setZoom(15) map.zoomIn() / map.zoomOut() map.fitBounds([[29,44],[39,63]]) // zoom خودکار به محدوده ایران // مختصات ↔ pixel map.latLngToContainerPoint([35.69, 51.39]) map.containerPointToLatLng([100, 200])
فاصله بین دو نقطه
const tehran = L.latLng(35.6892, 51.3890); const esfahan = L.latLng(32.6539, 51.6660); const dist = tehran.distanceTo(esfahan); console.log(Math.round(dist / 1000) + ' کیلومتر'); // ~340 کیلومتر // محاسبه zoom مناسب برای fit کردن دو نقطه const bounds = L.latLngBounds(tehran, esfahan); map.fitBounds(bounds, { padding: [50, 50] });
L.map('id').setView([lat,lng], zoom)— ساخت نقشهL.tileLayer(url).addTo(map)— لایه پایهL.marker([lat,lng]).addTo(map)— نشانگرL.circle / L.polygon / L.polyline— اشکالL.geoJSON(data).addTo(map)— داده جغرافیاییmap.on('click', fn)— رویدادL.control.layers({...}).addTo(map)— سوئیچ لایه
🚀زمین بازی کد (Playground)
کد Leaflet را همینجا اجرا کن و نقشه را زنده ببین. مارکر، اشکال و رویدادها را تغییر بده و ▶ اجرا را بزن. نسخهٔ ۱.۹.۴ آفلاین است؛ تایلهای نقشه به اینترنت نیاز دارند (آفلاین خاکستری میماند) ولی مارکرها و اشکال در هر حالت نمایش داده میشوند.