Leaflet ile Haritalamak
Python bazlı haritalama paketi Folium yazılımından daha önce [1]
bahsettik, aslında Folium paketinin ilk çıkışı Javascript bazlı
Leaflet yazılımıdır. Hatta Folium Python çağrıları sonrası save
ile
kaydedilebilen HTML dosyasına bakarsak orada leaflet Javascript
çağrıları olduğunu göreceğiz. Yani Folium aslında Leaflet'i
sarmalayan (wrap) ona arayüz sağlayan ince bir tabakadır.
Leaflet'in en avantajlı tarafı Javascript bazlı, yani tarayıcıda işleyen kodları sayesinde dinamik görüntülenen haritaları yaratabilmesidir. Yakınlaştırma (zoom), sağa sola kaydırma gibi pek çok işlemi bir Leaflet haritası üzerinde yapabiliriz. Ayrıca leaflet arka planda "fayans (tile)" denen görüntüleri basabilir, bunlar mevcut bir haritalama veri tabanı tarafından önceden üretilmiş farklı yakınlık seviyelerindeki görüntülerdir, yakınlık seviyesi kullanıcı tarafından arttırıldıkça gerekli görüntü haritalama servisinden alinip arka plana koyulur. Leaflet tüm bu işlemleri otomatik olarak yapar. Fayanslar mesela dağları, nehirler gösteren bir doğa tabakası olabilir, ya da tüm caddeleri, dükkanları gösteren bir şehir tabakası olabilir. Kullanıcı istediği fayans tabakasını haritayı oluştururken seçebilir.
Giriş
En basit leaflet kodu alttaki gibi,
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/>
<style>
#map {
height: 500px;
}
</style>
</head>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<script>
function init() {
map = L.map('map').setView([40,30], 6);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: 'OSM'
}).addTo(map);
}
</script>
<body onload='init()'>
<div id="map"></div>
</body>
</html>
Farklı fayans servisleri var, üstteki servis her ülkenin şehirlerini kendi alfabesi ile veriyor, eğer Latinize edilmiş isimler içeren haritalar görmek istiyorsak, Basemap servisinden,
https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png
adresini kullanabiliriz.
İşaretler
Bu haritaya gösterime sunmadan önce bazı ekler yapabiliriz. Mesela farklı renklerde işaretleyici (marker) kullanabiliriz. Bu işaretler için imaj dosyaları lazım, altta bu dosyalar bulunabilir,
marker-icon-2x-black.png, marker-icon-2x-blue.png, marker-icon-2x-gold.png, marker-icon-2x-green.png, marker-icon-2x-grey.png, marker-icon-2x-orange.png, marker-icon-2x-red.png, marker-icon-2x-violet.png, marker-icon-2x-yellow.png, marker-shadow.png
İşaretleyici ekleme kodları,
var LeafIcon = L.Icon.extend({
options: {
shadowUrl: 'marker-shadow.png',
iconSize: [20, 40],
shadowSize: [25, 30],
iconAnchor: [10, 45],
shadowAnchor: [2, 30],
popupAnchor: [-1, -30]
}
});
var orangeIcon = new LeafIcon({iconUrl: 'marker-icon-2x-orange.png'});
var yellowIcon = new LeafIcon({iconUrl: 'marker-icon-2x-yellow.png'});
var greenIcon = new LeafIcon({iconUrl: 'marker-icon-2x-green.png'});
L.marker([41,31], {icon: orangeIcon}).addTo(map);
L.marker([41,32], {icon: yellowIcon}).addTo(map);
L.marker([42,32], {icon: greenIcon}).addTo(map);
Üstteki işaretlere tıklanınca ortaya çıkan (popup) yazılar da
ekleyebilirdik, bunun için L.marker
sonrası .bindPopup("yazı").openPopup()
çağrısı yeterli, bu çağrı yine bir işaretleyici objesi geri döndürüyor
böylece o obje üzerinde hala addTo(map)
çağrısı yapabiliriz. Örnek,
Çizgiler, Poligonlar
Kordinat listesi vererek o noktaları birleştiren çizgiler çizebiliriz,
path = [[40,31],[41,31],[41,30]];
var line = new L.Polyline(path, {
color: 'red', weight: 3, opacity: 0.5, smoothFactor: 1
});
line.addTo(map);
Eğer verili noktalar bir poligon oluştursun istiyorsak leaflet bu noktaları bir kapalı alan olarak işleyebilir, listedeki son nokta ilk nokta ile birleştirilir, ve bizim verdiğimiz bir renk ile doldurulacak şekilde bir poligon çizilir. Üstteki aynı noktaları kullanarak şunu yapabilirdik,
var poly = new L.Polygon(path, {
color: 'red', weight: 3, opacity: 0.5, smoothFactor: 1
});
poly.setStyle({fillColor: '#0000FF'});
poly.addTo(map);
Tıklama Adresi
Haritada tıklanan yerdeki enlem/boylam kordinalarını almak için
map.on('click', function(e){
var coord = e.latlng;
var latclick = coord.lat;
var lngclick = coord.lng;
console.log(latclick,lngclick);
});
Boş Fayans
Arka planı yani fayans kısmını tamamen iptal edebiliriz, bunun faydası ne olur diye soranlar olabilir, fayda şurada, arka plan olmasa da sonradan eklenen çizgiler, noktalar hala büyütme, kaydırma kurallarına tabi oluyor, yani tamamen kendimizin yarattığı istenen yerine bakılabilen bir ağ yapısını burada çizebiliriz. Mesela ilk akla gelen örnek, fayans iptal edilir, ve bir JSON dosyasından tüm dünya kıtalarının sınırları alınıp çizmek, başka istenen ekler de yapılır, böylece kendimizin sıfırdan oluşturduğu bir harita yaratmış oluruz.
Basit bir boş fayans örneği için CSS içine
.leaflet-container { background-color: #109DE3; }
Kod seviyesinde
var base = {
'Empty': L.tileLayer(''),
'OpenStreetMap': L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
'attribution': 'Map data © OpenStreetMap contributors'
})
};
var map = L.map('map', {
'center': [40, 30],
'zoom': 7,
'layers': [
base.Empty
]
});
var control = L.control.layers(base).addTo(map);
path = [[40,31],[41,31],[41,30]];
var line = new L.Polyline(path, {
color: 'red', weight: 3, opacity: 0.5, smoothFactor: 1
});
line.addTo(map);
Fayans Servisi
Kendi fayans servisimizi de yazabiliriz. Leaflet'in arka plandaki
fayans servisi ile iletişimi direk, temiz bir yaklaşım, tileLayer
çağrısına geçilen parametreler haritanın belli bir parçasına nasıl
erişileceğini tarif ediyor, bu erişim basit dizin / dosya
üzerinden.. Makina ismi sonrası z,x,y parametreleri ile bir URL
oluşturuyor ve bu URL ile bir görüntü dosyası alınıyor (250x250
boyutunda), ki ünlü açık veri servisi OSM'nin zaten böyle bir servisi
var. Adresi https://tile.openstreetmap.org, dosya servisinin dizin
yapısında en üst dizinde büyüklük seviyesi, onun altındaki dizinde x
(boylam) dizinleri onun altında y (enlem) kordinatına tekabül eden
y.png dosyaları var. Bu bizi eğer mesela büyüklük seviyesi 4 boylam 11
enlem 7 ise bir
https://tile.openstreetmap.org/4/11/7.png
dosyasına eriştirecektir, mümkün her parametre kombinasyonu için bu dosya servisinde imaj dosyaları vardır.
Fakat arka planda illa bir dosya servisi sart degil, erişim parametrelerini bir servis kodu ile 'yakalayarak' kendi fayans servisimizi kod ile sağlayabiliriz. Flask ya da herhangi bir REST servisi ile makina / parametre1 / parametre2 / parametre3 gibi erişimlerin parametrelerini okuyabileceğimizi biliyoruz. o zaman önce leaflet'e kendi servis adresimizi veririz,
L.tileLayer('http://localhost:5000/tiles/{z}/{x}/{y}.jpg',...
Ardından servis kodlarımızı yazarız,
from flask import Flask, send_file
import os.path
app = Flask(__name__, static_url_path='/static')
@app.route('/tiles/<zoom>/<x>/<y>', methods=['GET', 'POST'])
def tiles(zoom, x, y):
tile1 = os.getcwd() + '/static/tile1.jpg'
tile2 = os.getcwd() + '/static/tile2.jpg'
print (tile1)
y = y.replace(".jpg","")
print ('zoom',zoom,'x',x,'y',y)
m = int(x+y+zoom) % 2
if m==0:
return send_file(tile1)
else:
return send_file(tile2)
@app.route('/', methods=['GET', 'POST'])
def index():
return app.send_static_file('index.html')
if __name__ == '__main__':
app.run(debug=True, host='localhost', port=5000)
Kod icin gereken iki imaj altta,
Bu dosyalar index.html
ile bir static
dizini altına yazılır,
servis için gereken app.py
bir üstteki dizindedir, standard Flask
yapısı bu.
Başlatılınca kullanım herhangi bir leaflet haritası kullanır gibi, görüntü olarak bazen daire bazen kare resimleri göreceğiz, hangi resmin servis edildiği x,y,zoom parametreleri birleştirilip sayının tek/çift olduğuna bakılarak yapılıyor, örnek amaçlı bir kod. Sayı tek ise bir dosya, çift ise diğeri servis ediliyor. Leaflet'in mekanizmasını anlamak açısından faydalı olabilir. Profosyonel bir uygulama servise gönderilen x,y,z parametrelerini işleyerek bir veri tabanından gerekli bir bilgiyi alıp görüntüyü yaratıp istemciye verebilir, leaflet bu görüntüleri anında yapıştırıp akıcı bir harita tecrübesi yaratabilir. Veri belki dağlar, belki nehirler, belki şehir isimleridir, tüm mümkün görüntüleri önceden yaratmak yerine belki bu şekilde veri bazlı bir işlem metotu daha uygundur.
Kaynaklar
[1] Haritalamak
[2] Stackoverflow
[3] Medium
Yukarı