dersblog

Genel Coğrafi Kordinat Kodları, HTML5, Javascript, Python

Mesafe hesabı yapmak

İki enlem, boylam kordinatı arasında mesafe hesabı için geopy kullanılabilir.

import geopy.distance
dist = geopy.distance.vincenty((51.238689, 4.406747),(51.232246, 4.444266))
print (dist)
print (dist.km)

İkinci ifade float tıpınde mesafeyi verir, kilometre bazlıdır.

Ya da

from pygeodesy.sphericalNvector import LatLon

p1 = LatLon(lat1, lon1)
p2 = LatLon(lat2, lon2)

p1.distanceTo(p2)

İki nokta arasında birinciden ikinciye olan açısal yön (bearing),

def get_bearing(lat1,lon1,lat2,lon2):
    dLon = lon2 - lon1;
    y = math.sin(dLon) * math.cos(lat2);
    x = math.cos(lat1)*math.sin(lat2) - math.sin(lat1)*math.cos(lat2)*math.cos(dLon);
    brng = np.rad2deg(math.atan2(y, x));
    if brng < 0: brng+= 360
    return brng

Sonuç 0 derece kuzey olmak üzere 0-360 derece arasında saat yönüne doğru artacak şekilde açı.

Bir kordinattan "10 km doğuya, batıya, vs. adım atınca nereye geliriz?" sorusunun cevabı için

import geopy
import geopy.distance
# baslangic noktasi
start = geopy.Point(48.853, 2.349)
# mesafe 1 km
d = geopy.distance.VincentyDistance(kilometers = 1)
# derece olarak adim atilacak yon, 0 derece kuzey, 90 dogu, ..
reached = d.destination(point=start, bearing=0)
print (reached.latitude)
print (reached.longitude)

Ya da

from pygeodesy.sphericalNvector import LatLon
clat,clon=39.06084392603182, 34.274201977299
DIST = 1000
p1 = LatLon(clat,clon)
EARTH_RAD = 6371
upright = p1.destination (DIST, bearing=45, radius=EARTH_RAD)
lowleft = p1.destination (DIST, bearing=225, radius=EARTH_RAD)
print ( upright )
print ( lowleft )
45.090707 N, 043.281808 E
32.450607 N, 026.747624 E

Örnekte Anadolu ortasından başlayıp 45 derece kuzeydoğuya ve 225 derece güneybatıya 1000 km adım atınca nereye geldiğimizi görüyoruz.

Orta Nokta

Bir GPS kordinat listesinin orta noktasını bulmak için, su bağlantıdan, enlem, boylam bir üç boyutlu vektör haline getiriliyor, ve Kartezyen bazlı bu vektörlerin ortalaması doğru ortalamayı veriyor. Kodun temel aldığı makale [1].

Grafiğe bakarsak yeryüzünde üç noktayı temsil eden A,B,C vektörleri bulunmuş bu vektörlerin her biri üç acı üzerinden temsil edilecek, ve eğer üç boyuttaki vektörlerin ortalamasını alırsak rahatlıkla M vektörünü bulabiliriz, çünkü vektörleri işaretlerinden tutun onların kapsamları aritmetiksel işlemler için uygundur. Kıyasla biri -90/+90 arasında diğeri -180/+180 arasında gidip gelen iki sayının aritmetiksel ortalamasının yeryüzünün her noktasında doğru sonucu vermesi zordur. Biri -90 sınırının bir tarafı diğeri öteki tarafında olan iki noktayı düşünebiliriz mesela..

Kodlama alttaki gibi olabilir,

import numpy as np
import numpy.linalg as lin

E = np.array([[0, 0, 1],[0, 1, 0],[-1, 0, 0]])

def lat_long2n_E(latitude,longitude):
    res = [np.sin(np.deg2rad(latitude)),
           np.sin(np.deg2rad(longitude)) * np.cos(np.deg2rad(latitude)),
           -np.cos(np.deg2rad(longitude)) * np.cos(np.deg2rad(latitude))]
    return np.dot(E.T,np.array(res))

def n_E2lat_long(n_E):
    n_E = np.dot(E, n_E)
    longitude=np.arctan2(n_E[1],-n_E[2]);
    equatorial_component = np.sqrt(n_E[1]**2 + n_E[2]**2 );
    latitude=np.arctan2(n_E[0],equatorial_component);
    return np.rad2deg(latitude), np.rad2deg(longitude)

def average(coords):
    res = []
    for lat,lon in coords:
        res.append(lat_long2n_E(lat,lon))
    res = np.array(res)
    m = np.mean(res,axis=0)
    m = m / lin.norm(m)
    return n_E2lat_long(m)


n = lat_long2n_E(30,20)
print (n)
print (n_E2lat_long(np.array(n)))

# fransa ve libya ortasi
coords = [[30,20],[47,3]]
m = average(coords)
print (m)

Üstteki işlemleri yapan bir paket te var,

from pygeodesy.sphericalNvector import LatLon
b = LatLon(45, 1), LatLon(45, 2), LatLon(46, 2), LatLon(46, 1)
nvecs = np.array([a.toNvector() for a in b])
print (nvecs)
mid = nvecs.mean().toLatLon()
print (mid.lat, mid.lon)
[Nvector(0.707, 0.01234, 0.70711) Nvector(0.70668, 0.02468, 0.70711)
 Nvector(0.69424, 0.02424, 0.71934) Nvector(0.69455, 0.01212, 0.71934)]
45.50109067812444 1.5
<class 'pygeodesy.sphericalNvector.LatLon'>
(0.700656, 0.018347, 0.713264)

Nokta Bir Alan İçinde mi

Elde köşeleri bilinen bir üçgen, kare ya da dışbükey poligon var diyelim, mesela Bermuda Üçgeni! Elimizdeki bir noktanın o alan içine düşüp düşmediğini nereden bileceğiz? Burada iyi bir yaklaşım pygeodesy paketi, bu paket üstteki n-vektör yaklaşımını kullanıyor, yani sağlam. Bir örnek altta, dörk köşesi verilmiş alan içine 45.1, 1.1 noktasının düşüp düşmediğini soruyoruz,

from pygeodesy.sphericalNvector import LatLon

p = LatLon(45.1, 1.1)
b = LatLon(45, 1), LatLon(45, 2), LatLon(46, 2), LatLon(46, 1)
print (p.isenclosedBy(b))

DMS ve Ondalik Formatlar Gecisi

Bazen 000 00' 05.31''W ve 51 28' 40.12'' N şeklinde enlem ve boylam verisi görebiliriz. Bu format yeri saat, dakika, saniye üzerinden göstermektedir. Çevirmek için pygeodesy içinde gerekli fonksiyonlar var,

from pygeodesy import parse3llh, fstr

x = parse3llh('000  00' 05.31''W, 51  28' 40.12'' N')
print (fstr(x, prec=6))
51.477811, -0.001475, 0.0

İlk iki sayı enlem ve boylamdır.

HTML5 ve Javascript ile Yer Bulmak

Javascript icinden yer bulmak mumkun, bu cep telefonunda da isliyor, Google'in Wifi, Telekom, GPS uzerinden yer bulan arayuzu ile baglantili zannederim. Kalitesini kontrol etmedim, ama alttaki kod isler ve yer rapor eder. Isletince tarayici 'yer bilgisine erisim' icin izin isteyecek. Izin verince (allow), bilgi sayfada basilacak ve kullanim ornegi olsun diye bir de URL baglantilardan birine parametre olarak eklenecek.

<html>
  <script>
    var lat = "lat";
    var lon = "lon";
    function getLocation() {
       navigator.geolocation.getCurrentPosition(setPosition);
    }
    function setPosition(position) {
      lat = position.coords.latitude;
      lon = position.coords.longitude;
      document.getElementById("locpos").innerHTML = lat + " " + lon;
      document.getElementById("url1").href="/bir/baglanti/" + lat + ";" + lon;
    }
</script>
<body onload="init()">

  <div class="navmenu">
    <nav>
      <div id="locpos">
        <p></p>
      </div>
      <ul>
        <li><a id="grab" href="#" onclick='getLocation()'>Yer Bul</a></li>
        <li><a id="url1" href="/bir/baglanti/32324">Baglanti</a></li>
        <li><a id="vsvs" href="">Vs..</a></li>
      </ul>
    </nav>
  </div>

</html>

İsimden Kordinat Bulmak

Bölge ve ülke ismi vererek dünyadaki pek çok yeri bulmak için geonames sitesi kullanılabilir, http://www.geonames.org adresine gidilip bilgiler girilince sonuçlar listeleniyor.

Script üzerinden bu bilgiyi nasıl çekeriz? Site URL parametresi olarak girdi kabul ediyor, eğer sonuçlardan ilkini çekip çıkartabilirek onu kordinat sonucu olarak alabiliriz. Mesela Ağrı Dağları için

http://www.geonames.org/search.html?q=Agri+Mountain&country=TR

Script uzerinden

from pygeodesy import parse3llh, fstr
import urllib.request as urllib2, re

def geoname(keyword, ccode):
    url = "http://www.geonames.org/search.html?q=%s&country=%s" % (keyword,ccode)    
    url = url.replace(" ","+")
    print (url)
    r = urllib2.urlopen(url).read()
    content = r.decode('utf-8')
    res = re.findall("Latitude.*?<td nowrap>(.*?)</td><td nowrap>(.*?)</td>",content, re.DOTALL)
    if len(res)==0: return False,99999,999999
    lats = res[0][1]; lons = res[0][0]
    lats = lats[1:] + lats[0]
    lons = lons[1:] + lons[0]
    geos = lats + "," + lons
    x = parse3llh(geos)
    res = fstr(x, prec=6).split(",")
    return True,float(res[0]),float(res[1])

print (geoname("Agri Mountain","TR"))

Sonuc

(True, 39.702222, 44.297778)

olarak çıkacak. İlk değer bir statü değeri, eğer veri işlemede problem çıkarsa problem bu şekilde iletilebilir.

Geocoder

Eğer üstteki yaklaşım fazla çetrefil gibi gözüküyorsa, geocoder adlı bir paket var,

import geocoder

g = geocoder.osm("Plymouth, MN")
print (g.latlng)
g = geocoder.osm("Istanbul, Turkey")
print (g.latlng)
g = geocoder.osm("Canakkale")
print (g.latlng)
[45.0065149, -93.46651]
[41.0091982, 28.9662187]
[40.0549886, 26.9278292]

Imajdan Kordinat Toplamak

Diyelim ki elimizde JPG üzerinde bir harita var, üzerinde bir sınır gösterilmiş, ve oradan sınır verilerini toplamak istiyoruz. Alttaki basit GUI kodu şunu yapar 1) Parametre olarak geçilen resmi gösterir, 2) O resim üzerinde yapılacak fare tıklamalarının x,y piksel kordinatlarını ekrana basar. Bu sayede çıplak gözle sınıra bakarız, üzerine tıklarız, ve kordinatlarını "geriye mühendislik" ile toplamış oluruz.

from PIL import ImageDraw, Image, ImageTk
import sys, tkinter

window = tkinter.Tk(className="bla")
image = Image.open(sys.argv[1])
print ('size',image.size[0],image.size[1])

image = image.resize((int(1000), int(700)), Image.ANTIALIAS)
canvas = tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)

canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)

def callback(event):
    print ("[%d,%d]," % (event.x, event.y))

canvas.bind("<Button-1>", callback)
tkinter.mainloop()

Gerekli paketi kurmak için sudo apt install python3-tk yeterli.

Tabii nihai kordinatlar için ek bir adım daha gerekli, piksel değerlerini alıp enlem, boylama çevirmek; bu hesap için enlem boylamını bildiğimiz iki piksel değeri yeterli. Alttaki gibi bir kodla bu transformasyon yapılabilir. Referans iki kordinat değeri refc, ona tekabül eden iki piksel x,y değeri refp içinde, ve toplanan tüm piksel değerleri bir liste olarak clicks içinde.

def platlon(refc, refp, clicks):
    mlat = (refc[1,0]-refc[0,0]) / (refp[1,1]-refp[0,1])
    mlon = (refc[1,1]-refc[0,1]) / (refp[1,0]-refp[0,0])
    cs = [ [ refc[0,0]+(y-refp[0,1])*mlat, refc[0,1]+(x-refp[0,0])*mlon ] for x,y in data ]
    return cs

Kaynaklar

[1] Kenneth Gade (2010), A Non-singular Horizontal Position Representation, The Journal of Navigation, Volume 63, Issue 03, pp 395-417, July 2010.


Yukarı