Coğrafi Kodlama API'si Web Hizmetlerini Kullanmaya Yönelik En İyi Uygulamalar

Google Haritalar Platformu web hizmetleri, harita uygulamalarınız için coğrafi veriler sağlayan Google hizmetlerine yönelik HTTP arayüzleri koleksiyonudur.

Bu kılavuzda, web hizmetinizin isteklerini ayarlamak ve hizmet yanıtlarını işlemek için faydalı olabilecek bazı yaygın uygulamalar açıklanmaktadır. Geocoding API'nin tam dokümanları için geliştirici kılavuzuna bakın.

Web hizmeti nedir?

Google Haritalar Platformu web hizmetleri, harici hizmetlerden Maps API verileri istemek ve bu verileri Haritalar uygulamalarınızda kullanmak için kullanılan bir arayüzdür. Bu hizmetler, Google Haritalar Platformu Hizmet Şartları'ndaki Lisans Kısıtlamaları uyarınca bir harita ile birlikte kullanılmak üzere tasarlanmıştır.

Maps API'lerin web hizmetleri, belirli URL'lere HTTP(S) istekleri göndererek URL parametrelerini ve/veya JSON biçimli POST verilerini hizmetlere bağımsız değişken olarak iletir. Bu hizmetler genellikle, uygulamanız tarafından ayrıştırılması ve/veya işlenmesi için yanıt gövdesinde verileri JSON veya XML olarak döndürür.

Tipik bir Geocoding API isteği genellikle aşağıdaki biçimdedir:

https://maps.googleapis.com/maps/api/geocode/output?parameters

Burada output, yanıt biçimini (genellikle json veya xml) belirtir.

Not: Tüm Geocoding API uygulamaları için kimlik doğrulama gerekir. Kimlik doğrulama kimlik bilgileri hakkında daha fazla bilgi edinin.

SSL/TLS Erişimi

API anahtarları kullanan veya kullanıcı verileri içeren tüm Google Haritalar Platformu isteklerinde HTTPS gereklidir. HTTP üzerinden yapılan ve hassas veriler içeren istekler reddedilebilir.

Geçerli bir URL oluşturma

"Geçerli" bir URL'nin ne olduğunu anlamak kolay gibi görünse de durum tam olarak böyle değil. Örneğin, bir tarayıcıdaki adres çubuğuna girilen bir URL özel karakterler (ör."上海+中國") içerebilir. Tarayıcının, bu karakterleri iletmeden önce dahili olarak farklı bir kodlamaya çevirmesi gerekir. Aynı şekilde, UTF-8 girişi oluşturan veya kabul eden herhangi bir kod, UTF-8 karakterleri içeren URL'leri "geçerli" olarak değerlendirebilir ancak bu karakterleri bir web sunucusuna göndermeden önce çevirmesi de gerekir. Bu işleme URL kodlama veya yüzde kodlama denir.

Özel karakterler

Tüm URL'lerin Tekdüzen Kaynak Tanımlayıcı (URI) spesifikasyonu tarafından belirtilen söz dizimi kurallarına uyması gerektiğinden özel karakterleri çevirmemiz gerekir. Bu, URL'lerin yalnızca ASCII karakterlerinin özel bir alt kümesini içermesi gerektiği anlamına gelir: Bilinen alfanümerik semboller ve URL'lerde kontrol karakteri olarak kullanılmak üzere ayrılmış bazı karakterler. Bu tabloda bu karakterler özetlenmiştir:

Geçerli URL Karakterlerinin Özeti
HazırkarakterlerURL kullanımı
Alfanümerik a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 Metin dizeleri, şema kullanımı (http), bağlantı noktası (8080) vb.
Ayrılmamış - _ . ~ Metin dizeleri
Rezervasyon yapıldı ! * ' ( ) ; : @ & = + $ , / ? % # [ ] Kontrol karakterleri ve/veya metin dizeleri

Geçerli bir URL oluştururken yalnızca tabloda gösterilen karakterleri içerdiğinden emin olmanız gerekir. Bir URL'nin bu karakter grubunu kullanacak şekilde düzenlenmesi genellikle iki soruna yol açar: biri atlama, diğeri de değiştirme.

  • İşlemek istediğiniz karakterler yukarıdaki grubun dışındaysa Örneğin, 上海+中國 gibi yabancı dillerdeki karakterlerin yukarıdaki karakterler kullanılarak kodlanması gerekir. Yaygın bir kurala göre, boşluklar (URL'lerde izin verilmez) genellikle artı '+' karakteri kullanılarak da temsil edilir.
  • Yukarıdaki kümede ayrılmış karakterler olarak bulunan karakterlerin, olduğu gibi kullanılması gerekir. Örneğin, ?, sorgu dizesinin başlangıcını belirtmek için URL'lerde kullanılır. "? and the Mysterions" dizesini kullanmak istiyorsanız '?' karakterini kodlamanız gerekir.

URL kodlaması yapılacak tüm karakterler, '%' karakteri ve UTF-8 karakterlerine karşılık gelen iki karakterli bir onaltılık değer kullanılarak kodlanır. Örneğin, UTF-8'deki 上海+中國, URL olarak %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B şeklinde kodlanır. ? and the Mysterians dizesi, URL kodlamalı olarak %3F+and+the+Mysterians veya %3F%20and%20the%20Mysterians şeklinde olur.

Kodlama gerektiren yaygın karakterler

Kodlanması gereken bazı yaygın karakterler şunlardır:

Güvenli olmayan karakter Kodlanmış değer
Boşluk %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

Kullanıcı girişinden aldığınız bir URL'yi dönüştürmek bazen zor olabilir. Örneğin, bir kullanıcı "5. Cadde&Ana Cadde" şeklinde bir adres girebilir. Genel olarak, URL'nizi parçalarından oluşturmalı ve tüm kullanıcı girişlerini gerçek karakterler olarak değerlendirmelisiniz.

Ayrıca, tüm Google Haritalar Platformu web hizmetleri ve statik web API'leri için URL'ler 16.384 karakterle sınırlıdır. Çoğu hizmette bu karakter sınırına nadiren yaklaşılır. Ancak bazı hizmetlerin uzun URL'lere neden olabilecek çeşitli parametreleri olduğunu unutmayın.

Google API'lerinin Uygun Kullanımı

Kötü tasarlanmış API istemcileri hem internete hem de Google'ın sunucularına gereğinden fazla yük bindirebilir. Bu bölümde, API istemcileri için bazı en iyi uygulamalar yer almaktadır. Bu en iyi uygulamalardan yararlanmak, API'lerin yanlışlıkla kötüye kullanılması nedeniyle uygulamanızın engellenmesini önlemenize yardımcı olabilir.

Hataları ve yeniden denemeleri yönetme

Geocoding API'den gelen UNKNOWN_ERROR veya OVER_QUERY_LIMIT yanıt kodları hakkında bilgi edinmek için hataları ve yeniden denemeleri yönetme başlıklı makaleyi okuyun.

Üstel Geri Alma

Nadir durumlarda isteğinizin sunumu sırasında bir sorun yaşanabilir. 4XX veya 5XX HTTP yanıt kodu alabilir ya da TCP bağlantısı istemciniz ile Google'ın sunucusu arasında bir noktada başarısız olabilir. Orijinal istek başarısız olduğunda takip isteği başarılı olabileceğinden isteği tekrar denemek genellikle faydalıdır. Ancak, Google'ın sunucularına tekrar tekrar istek göndermek yerine, bu işlemi döngü içinde yapmamak önemlidir. Bu döngü davranışı, istemciniz ile Google arasındaki ağı aşırı yükleyebilir ve birçok taraf için soruna neden olabilir.

Daha iyi bir yaklaşım, denemeler arasında artan gecikmelerle yeniden denemektir. Genellikle gecikme, her denemeyle birlikte çarpma faktörü kadar artırılır. Bu yaklaşıma eksponansiyel geri yükleme adı verilir.

Örneğin, Time Zone API'ye şu isteği göndermek isteyen bir uygulamayı düşünün:

https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

Aşağıdaki Python örneğinde, isteğin üstel geri çekme ile nasıl yapılacağı gösterilmektedir:

import json
import time
import urllib.error
import urllib.parse
import urllib.request

# The maps_key defined below isn't a valid Google Maps API key.
# You need to get your own API key.
# See https://developers.google.com/maps/documentation/timezone/get-api-key
API_KEY = "YOUR_KEY_HERE"
TIMEZONE_BASE_URL = "https://maps.googleapis.com/maps/api/timezone/json"


def timezone(lat, lng, timestamp):

    # Join the parts of the URL together into one string.
    params = urllib.parse.urlencode(
        {"location": f"{lat},{lng}", "timestamp": timestamp, "key": API_KEY,}
    )
    url = f"{TIMEZONE_BASE_URL}?{params}"

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 5  # Set the maximum retry delay to 5 seconds.

    while True:
        try:
            # Get the API response.
            response = urllib.request.urlopen(url)
        except urllib.error.URLError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.load(response)

            if result["status"] == "OK":
                return result["timeZoneId"]
            elif result["status"] != "UNKNOWN_ERROR":
                # Many API errors cannot be fixed by a retry, e.g. INVALID_REQUEST or
                # ZERO_RESULTS. There is no point retrying these requests.
                raise Exception(result["error_message"])

        if current_delay > max_delay:
            raise Exception("Too many retry attempts.")

        print("Waiting", current_delay, "seconds before retrying.")

        time.sleep(current_delay)
        current_delay *= 2  # Increase the delay each time we retry.


if __name__ == "__main__":
    tz = timezone(39.6034810, -119.6822510, 1331161200)
    print(f"Timezone: {tz}")

Ayrıca, uygulama çağrısı zincirinin üst kısmında, hızlı bir şekilde art arda tekrarlanan isteklere neden olan bir yeniden deneme kodu olmadığından emin olmanız gerekir.

Senkronize İstekler

Google'ın API'lerine gönderilen çok sayıda senkronize istek, Google'ın altyapısına yönelik bir Dağıtılmış Hizmet Reddi (DDoS) saldırısı gibi görünebilir ve buna göre ele alınabilir. Bunu önlemek için API isteklerinin istemciler arasında senkronize edilmediğinden emin olmanız gerekir.

Örneğin, saati geçerli saat diliminde gösteren bir uygulamayı düşünün. Bu uygulama, gösterilen zamanın güncellenebilmesi için istemci işletim sisteminde dakikanın başında istemciyi uyandıran bir alarm ayarlayabilir. Uygulama, bu alarmla ilişkili işlem kapsamında API çağrısı yapmamalıdır.

Sabit bir alarma yanıt olarak API çağrıları yapmak kötüdür çünkü API çağrılarının zaman içinde eşit olarak dağıtılması yerine farklı cihazlar arasında bile dakikanın başlangıcıyla senkronize edilmesine neden olur. Kötü tasarlanmış bir uygulama, her dakikanın başında normal seviyelerin altmış katı kadar trafik sıçraması oluşturur.

Bunun yerine, rastgele seçilen bir saate ayarlanmış ikinci bir alarmın olması iyi bir tasarım olabilir. Bu ikinci alarm tetiklendiğinde uygulama, ihtiyaç duyduğu tüm API'leri çağırır ve sonuçları depolar. Uygulama, dakikanın başında ekranını güncellemek istediğinde API'yi tekrar çağırmak yerine önceden depolanmış sonuçları kullanır. Bu yaklaşımda API çağrıları zaman içinde eşit şekilde dağıtılır. Ayrıca, API çağrıları ekran güncellenirken oluşturmayı geciktirmez.

Dakikanın başlangıcı dışında, senkronizasyonu hedeflememeniz gereken diğer yaygın zaman aralıkları, bir saatin başlangıcı ve her günün başlangıcı olan gece yarısıdır.

Yanıtları işleme

Bu bölümde, bu değerlerin web hizmeti yanıtlarından dinamik olarak nasıl çıkarılacağı açıklanmaktadır.

Google Haritalar web hizmetleri, anlaşılması kolay ancak tam olarak kullanıcı dostu olmayan yanıtlar sağlar. Sorgu yaparken bir veri kümesini görüntülemek yerine, muhtemelen birkaç belirli değeri ayıklamak istersiniz. Genellikle, web hizmetinden gelen yanıtları ayrıştırmak ve yalnızca ilgilendiğiniz değerleri ayıklamak istersiniz.

Kullandığınız ayrıştırma şeması, çıkışı XML mi yoksa JSON biçiminde mi döndürdüğünüze bağlıdır. JSON yanıtları zaten JavaScript nesnesi biçiminde olduğundan istemcide JavaScript'in kendisinde işlenebilir. XML yanıtları, XML biçimindeki öğeleri adreslemek için bir XML işlemcisi ve XML sorgu dili kullanılarak işlenmelidir. XML işleme kitaplıklarında yaygın olarak desteklendiğinden aşağıdaki örneklerde XPath kullanıyoruz.

XML'i XPath ile işleme

XML, veri alışverişi için kullanılan nispeten gelişmiş bir yapılandırılmış bilgi biçimidir. JSON kadar hafif olmasa da XML daha fazla dil desteği ve daha güçlü araçlar sunar. Örneğin, Java'da XML'i işleme kodu javax.xml paketlerine yerleştirilmiştir.

XML yanıtlarını işlerken, öğelerin XML işaretlemesindeki mutlak konumlarda olduğunu varsaymak yerine XML dokümanındaki düğümleri seçmek için uygun bir sorgu dili kullanmanız gerekir. XPath, XML belgesindeki düğümleri ve öğeleri benzersiz şekilde tanımlamak için kullanılan bir dil söz dizimidir. XPath ifadeleri, XML yanıt belgesindeki belirli içeriği tanımlamanıza olanak tanır.

XPath İfadeleri

XPath hakkında bilgi sahibi olmak, güçlü bir ayrıştırma şeması geliştirmek için çok faydalıdır. Bu bölümde, bir XML belgesindeki öğelerin XPath ile nasıl adreslendiği ele alınacaktır. Bu sayede birden fazla öğeyi adresleyebilir ve karmaşık sorgular oluşturabilirsiniz.

XPath, XML dokümanındaki öğeleri seçmek için dizin yollarında kullanılana benzer bir söz dizimi kullanarak ifadeler kullanır. Bu ifadeler, DOM'a benzer hiyerarşik bir ağaç olan XML doküman ağacındaki öğeleri tanımlar. Genel olarak XPath ifadeleri açgözlüdür ve sağlanan ölçütlerle eşleşen tüm düğümlerle eşleşeceğini belirtir.

Örneklerimizi açıklamak için aşağıdaki soyut XML'i kullanacağız:

<WebServiceResponse>
 <status>OK</status>
 <result>
  <type>sample</type>
  <name>Sample XML</name>
  <location>
   <lat>37.4217550</lat>
   <lng>-122.0846330</lng>
  </location>
 </result>
 <result>
  <message>The secret message</message>
 </result>
</WebServiceResponse>

İfadelerde Düğüm Seçimi

XPath seçimleri düğümleri seçer. Kök düğüm, dokümanın tamamını kapsar. Bu düğümü "/" özel ifadesini kullanarak seçersiniz. Kök düğümün, XML dokümanınızın üst düzey düğümü olmadığını unutmayın. Aslında bu üst düzey öğenin bir seviye üzerinde bulunur ve bu öğeyi içerir.

Öğe düğümleri, XML belge ağacındaki çeşitli öğeleri temsil eder. Örneğin, <WebServiceResponse> öğesi, yukarıdaki örnek hizmetimizde döndürülen üst düzey öğeyi temsil eder. Düğümleri, başlangıçta "/" karakterinin bulunması veya bulunmamasıyla gösterilen mutlak veya göreli yollar aracılığıyla seçersiniz.

  • Mutlak yol: "/WebServiceResponse/result" ifadesi, <WebServiceResponse> düğümünün alt öğeleri olan tüm <result> düğümlerini seçer. (Bu öğelerin her ikisinin de "/" kök düğümünden geldiğini unutmayın.)
  • Geçerli bağlamdan göreli yol: "result" ifadesi, geçerli bağlamdaki tüm <result> öğeleriyle eşleşir. Genellikle web hizmeti sonuçlarını tek bir ifadeyle işlediğiniz için bağlam konusunda endişelenmenize gerek yoktur.

Bu ifadelerden herhangi biri, çift eğik çizgi ("//") ile gösterilen bir joker karakter yolu eklenerek genişletilebilir. Bu joker karakter, araya giren yolda sıfır veya daha fazla öğenin eşleşebileceğini gösterir. Örneğin,"//formatted_address" XPath ifadesi, geçerli belgedeki bu ada sahip tüm düğümlerle eşleşir. //viewport//lat ifadesi, <viewport> öğesini üst öğe olarak izleyebilen tüm <lat> öğeleriyle eşleşir.

XPath ifadeleri varsayılan olarak tüm öğelerle eşleşir. Kare içine alınmış ([]) bir öznitelik sağlayarak ifadeyi belirli bir öğeyle eşleşecek şekilde kısıtlayabilirsiniz. Örneğin, "/GeocodeResponse/result[2]" XPath ifadesi her zaman ikinci sonucu döndürür.

İfade Türü
Root düğüm
XPath ifadesi:  "/"
Seçim:
    <WebServiceResponse>
     <status>OK</status>
     <result>
      <type>sample</type>
      <name>Sample XML</name>
      <location>
       <lat>37.4217550</lat>
       <lng>-122.0846330</lng>
      </location>
     </result>
     <result>
      <message>The secret message</message>
     </result>
    </WebServiceResponse>
    
Mutlak Yol
XPath ifadesi:  "/WebServiceResponse/result"
Seçim:
    <result>
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    </result>
    <result>
     <message>The secret message</message>
    </result>
    
Joker karakter içeren yol
XPath ifadesi:  "/WebServiceResponse//location"
Seçim:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
Koşul içeren yol
XPath ifadesi:  "/WebServiceResponse/result[2]/message"
Seçim:
    <message>The secret message</message>
    
İlk result öğesinin tüm doğrudan alt öğeleri
XPath ifadesi:  "/WebServiceResponse/result[1]/*"
Seçim:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
type metni "sample" olan bir result'un name değeri.
XPath ifadesi:  "/WebServiceResponse/result[type/text()='sample']/name"
Seçim:
    Sample XML
    

Öğeleri seçerken yalnızca bu nesnelerdeki metni değil, düğümleri de seçtiğinizi unutmayın. Genellikle, eşleşen tüm düğümleri iterasyon yaparak dolaşıp metni ayıklamak istersiniz. Metin düğümlerini doğrudan da eşleştirebilirsiniz. Aşağıdaki Metin Düğümleri bölümüne bakın.

XPath'in özellik düğümlerini de desteklediğini unutmayın. Ancak tüm Google Haritalar web hizmetleri, özellik içermeyen öğeler sunar. Bu nedenle, özelliklerin eşleştirilmesi gerekli değildir.

İfadelerde Metin Seçimi

XML dokümanındaki metin, XPath ifadelerinde metin düğümü operatörü aracılığıyla belirtilir. Bu operatör ("text()"), belirtilen düğümden metin ayıklandığını gösterir. Örneğin, "//formatted_address/text()" XPath ifadesi, <formatted_address> öğelerindeki tüm metni döndürür.

İfade Türü
Tüm metin düğümleri (boşluklar dahil)
XPath ifadesi:  "//text()"
Seçim:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
Metin Seçimi
XPath ifadesi:  "/WebServiceRequest/result[2]/message/text()"
Seçim:
    The secret message
    
Bağlama duyarlı seçim
XPath ifadesi:  "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
Seçim:
    Sample XML
    

Alternatif olarak, bir ifadeyi değerlendirip bir düğüm grubu döndürebilir ve ardından bu "düğüm grubu"nu iterleyerek her düğümdeki metni ayıklayabilirsiniz. Aşağıdaki örnekte bu yaklaşımı kullanıyoruz.

XPath hakkında daha fazla bilgi için XPath W3C Spesifikasyonu'na bakın.

Java'da XPath'i değerlendirme

Java, XML'i ayrıştırma ve javax.xml.xpath.* paketinde XPath ifadeleri kullanma konusunda geniş destek sunar. Bu nedenle, bu bölümdeki örnek kodda XML'in nasıl işleneceğini ve XML hizmet yanıtlarından gelen verilerin nasıl ayrıştırılacağını göstermek için Java kullanılır.

Java kodunuzda XPath'i kullanmak için öncelikle bir XPathFactory örneği oluşturmanız ve XPath nesnesi oluşturmak için bu fabrikada newXPath()'yi çağırmanız gerekir. Bu nesne daha sonra evaluate() yöntemini kullanarak iletilen XML ve XPath ifadelerini işleyebilir.

XPath ifadelerini değerlendirirken, döndürülebilecek tüm olası "düğüm kümelerini" iterasyonla gezdiğinizden emin olun. Bu sonuçlar Java kodunda DOM düğümü olarak döndürüldüğünden, bu tür birden çok değeri bir NodeList nesnesi içinde yakalamanız ve bu düğümlerden metin veya değerler ayıklamak için bu nesne üzerinde iterasyon yapmanız gerekir.

Aşağıdaki kodda, bir XPath nesnesinin nasıl oluşturulacağı, bu nesneye XML ve XPath ifadesi atanacağı ve ilgili içeriği yazdırmak için ifadenin nasıl değerlendirileceği gösterilmektedir.

import org.xml.sax.InputSource;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import java.io.*;

public class SimpleParser {

  public static void main(String[] args) throws IOException {

	XPathFactory factory = XPathFactory.newInstance();

    XPath xpath = factory.newXPath();

    try {
      System.out.print("Web Service Parser 1.0\n");

      // In practice, you'd retrieve your XML via an HTTP request.
      // Here we simply access an existing file.
      File xmlFile = new File("XML_FILE");

      // The xpath evaluator requires the XML be in the format of an InputSource
	  InputSource inputXml = new InputSource(new FileInputStream(xmlFile));

      // Because the evaluator may return multiple entries, we specify that the expression
      // return a NODESET and place the result in a NodeList.
      NodeList nodes = (NodeList) xpath.evaluate("XPATH_EXPRESSION", inputXml, XPathConstants.NODESET);

      // We can then iterate over the NodeList and extract the content via getTextContent().
      // NOTE: this will only return text for element nodes at the returned context.
      for (int i = 0, n = nodes.getLength(); i < n; i++) {
        String nodeString = nodes.item(i).getTextContent();
        System.out.print(nodeString);
        System.out.print("\n");
      }
    } catch (XPathExpressionException ex) {
	  System.out.print("XPath Error");
    } catch (FileNotFoundException ex) {
      System.out.print("File Error");
    }
  }
}