Servicios web de las Google Maps API

Los servicios web de Google Maps son un conjunto de interfaces HTTP para los servicios de Google que proporcionan datos geográficos para tus aplicaciones de mapas. Esta guía solo introduce los servicios web y proporciona información común a todos los diferentes servicios. A continuación se proporcionan vínculos a la documentación individual para cada servicio:

Averigua qué API necesitas

Usa el seleccionador de API para averiguar cuál es la API correcta para tu proyecto.

Más información acerca de los servicios web

El resto de esta guía discute técnicas para configurar solicitudes de servicios web y analizar las respuestas. No obstante, para obtener información específica para cada servicio, debes consultar la documentación correspondiente.

¿Qué es un servicio web?

La Google Maps API proporciona estos servicios web como de una interfaz para solicitar datos de Maps API a servidores externos y usarlos dentro de tus aplicaciones de Maps. Estos servicios están diseñados para usarse junto con un mapa, de acuerdo con las Restricciones de la licencia para las condiciones de servicio de las Google Maps API.

Los servicios web usan solicitudes HTTP para direcciones URL específicas y les pasan a los servicios parámetros de direcciones URL como argumentos. Generalmente, los servicios devuelven datos en la solicitud HTTP como JSON o XML para que tu aplicación los analice o los procese.

Una solicitud de servicio web típica generalmente tiene el siguiente aspecto:

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

donde service indica el servicio específico solicitado y output indica el formato de la respuesta (generalmente json o xml).

Puedes encontrar información completa acerca de cada servicio en las guías del desarrollador específicas para esos servicios. No obstante, esta guía aborda algunas prácticas comunes que resultan útiles para configurar tus solicitudes de servicios web y procesar las respuestas de los servicios web.

Acceso SSL

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

Se requiere HTTPS para todas las solicitudes de servicios web de Maps API que contengan datos del usuario o identificadores de desarrollador. Las solicitudes realizadas a través de HTTP que incluyan datos confidenciales se rechazarán.

Creación de una dirección URL válida

Puedes pensar que una dirección URL “válida” es evidente, pero no es el caso. Una dirección URL insertada a una barra de dirección en un navegador, por ejemplo, puede contener caracteres especiales (p. ej., "上海+中國"); el navegador debe traducir internamente esos caracteres a una codificación diferente antes de la transmisión. Con el mismo token, todo código que genere o acepte entradas en UTF-8 podría procesar las direcciones URL con caracteres UTF-8 como “válidas”, pero también necesitaría traducir esos caracteres antes de enviarlos a un servidor web. Este proceso se llama codificación de direcciones URL.

Debemos traducir los caracteres especiales porque todas las direcciones URL deben cumplir con la sintaxis que se indica en la especificación Identificador de recursos uniformes W3. En efecto, esto significa que las direcciones URL deben contener solo un subconjunto especial de caracteres ASCII: los símbolos alfanuméricos que ya conocemos y algunos caracteres reservados para usar como caracteres de control en las direcciones URL. La siguiente tabla resume esos caracteres:

Resumen de caracteres válidos para direcciones URL
ConjuntocaracteresUso de dirección URL
Alfanuméricos 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 Cadenas de texto, uso de esquemas (http), puerto (8080), etc.
No reservados - _ . ~ Cadenas de texto
Reservados ! * ' ( ) ; : @ & = + $ , / ? % # [ ] Caracteres de control o cadenas de texto

Al crear una dirección URL válida, debes asegurarte de que contenga solo los caracteres que se muestran más arriba. Adaptar una dirección URL para usar este conjunto de caracteres generalmente provoca dos problemas, una omisión y una sustitución:

  • Quizá quieras usar caracteres que no se encuentran dentro del conjunto anterior. Por ejemplo, los caracteres en otros idiomas, como 上海+中國, se deben codificar usando los caracteres anteriores. Por convención popular, los espacios (que no se permiten en las direcciones URL) con frecuencia también se representan con el carácter '+'.
  • Hay caracteres dentro del conjunto anterior que son caracteres reservados, pero se deben usar literalmente. Por ejemplo, ? se usa en direcciones URL para indicar el inicio de la cadena de consulta; si quieres usar la cadena "? and the Mysterions", debes codificar el carácter '?'.

La codificación de caracteres para direcciones URL usa un carácter '%' y un valor hexadecimal de dos caracteres correspondientes a su carácter en UTF-8. Por ejemplo, 上海+中國 en UTF-8 se codificaría para una dirección URL como %E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B. La cadena ? and the Mysterians se codificaría para la dirección URL como %3F+and+the+Mysterians.

A continuación se indican algunos de los caracteres que se deben codificar:

Caracteres no seguros Valor codificado
Espacio %20
" %22
< %3C
> %3E
# %23
% %25
| %7C

Convertir una dirección URL que recibes a través de la entrada de un usuario suele ser engañoso. Por ejemplo, un usuario puede ingresar una dirección como "5th&Main St.". Generalmente, deberías construir tu dirección URL a partir de sus partes y tratar las entradas de un usuario como caracteres literales.

Además, las URL tienen una limitación de 8192 caracteres para todos los servicios web. Para la mayoría de los servicios, este límite de caracteres rara vez se alcanza. No obstante, ten en cuenta que algunos servicios tienen varios parámetros que podrían dar lugar a direcciones URL extensas.

Uso normal de las API de Google

Los clientes de API de diseño deficiente pueden presentar una mayor carga que la necesaria en Internet y en los servidores de Google. En esta sección se explican algunas de las prácticas recomendadas para los clientes de las API. Seguir estas prácticas recomendadas puede ayudarte a evitar que tu aplicación se bloquee debido a abusos accidentales de las API.

Interrupción exponencial

En raras ocasiones, puede producirse un error en tu solicitud. Es posible que recibas un código de respuesta HTTP 4XX o 5XX, o que la conexión TCP simplemente falle en algún punto entre tu cliente y el servidor de Google. A menudo, vale la pena volver a intentar realizar la solicitud debido a que, cuando falle la primera la solicitud, la siguiente puede ser exitosa. No obstante, es importante no realizar varias solicitudes repetidas a los servidores de Google. Este comportamiento repetitivo puede sobrecargar la red entre tu cliente y Google, y causar problemas para varias partes.

Un mejor enfoque consiste en realizar nuevos intentos con demoras más prolongadas entre uno y otro. Generalmente, la demora aumenta conforme a un factor multiplicativo con cada intento; un enfoque conocido como interrupción exponencial.

Por ejemplo, piensa en una aplicación que desee realizar esta solicitud a la Google Maps Time Zone API:

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

En el siguiente ejemplo de Python se muestra la manera de realizar la solicitud con interrupción exponencial:

import json
import time
import urllib
import urllib2

def timezone(lat, lng, timestamp):
    # 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
    maps_key = 'YOUR_KEY_HERE'
    timezone_base_url = 'https://maps.googleapis.com/maps/api/timezone/json'

    # This joins the parts of the URL together into one string.
    url = timezone_base_url + '?' + urllib.urlencode({
        'location': "%s,%s" % (lat, lng),
        'timestamp': timestamp,
        'key': maps_key,
    })

    current_delay = 0.1  # Set the initial retry delay to 100ms.
    max_delay = 3600  # Set the maximum retry delay to 1 hour.

    while True:
        try:
            # Get the API response.
            response = str(urllib2.urlopen(url).read())
        except IOError:
            pass  # Fall through to the retry loop.
        else:
            # If we didn't get an IOError then parse the result.
            result = json.loads(response.replace('\\n', ''))
            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.

tz = timezone(39.6034810, -119.6822510, 1331161200)
print 'Timezone:', tz

También debes tener la precaución de que no haya un código de reintento más alto en la cadena de llamada de la aplicación que provoque solicitudes repetidas en una sucesión rápida.

Solicitudes sincronizadas

Grandes cantidades de solicitudes sincronizadas a las Google API pueden percibirse como un ataque de denegación de servicio distribuido (DDoS) en la infraestructura de Google, y abordarse en consecuencia. Para evitar esto, debes asegurarte de que las solicitudes de API no se sincronicen entre clientes.

Por ejemplo, considera una aplicación que muestre la hora en la zona horaria actual. Esta aplicación probablemente configurará una alarma en el sistema operativo del cliente que lo despierte al inicio del minuto, de modo que la hora que se muestra pueda actualizarse. La aplicación no debe realizar llamadas de API como parte del procesamiento asociado con esa alarma.

Realizar llamadas de API en respuesta a una alarma fija no es recomendable, ya que provoca que las llamadas de API se sincronicen al inicio del minuto, incluso entre diferentes dispositivos, en lugar de distribuirse de forma pareja con el tiempo. Una aplicación mal diseñada que hace esto producirá un pico de tráfico al inicio de cada minuto que equivale a sesenta veces los niveles normales.

En su lugar, un posible buen diseño es tener una segunda alarma configurada en una hora elegida al azar. Cuando esta segunda alarma se activa, la aplicación llama a las API que necesita y almacena los resultados. Cuando la aplicación intenta actualizar la pantalla al inicio del minuto, usa los resultados que almacenó previamente en lugar de volver a llamar a la API. Mediante este enfoque, las llamadas de API se distribuyen de forma equitativa con el tiempo. Además, las llamadas de API no demoran la representación cuando se actualiza la pantalla.

Además de la sincronización al inicio del minuto, con otros momentos de sincronización comunes se debe tener la precaución de que no se produzcan al inicio de la hora y al inicio de cada día a la medianoche.

Procesamiento de respuestas

Dado que no se garantiza el formato exacto de respuestas individuales con una solicitud de servicio web (pueden faltar algunos elementos o encontrarse en múltiples ubicaciones), nunca debes asumir que el formato devuelto para cualquier respuesta será el mismo para otras consultas. En su lugar, debes procesar la respuesta y seleccionar valores adecuados a través de expresiones. En esta sección se discute cómo extraer esos valores de forma dinámica de las respuestas de los servicios web.

Los servicios web de Google Maps proporcionan respuestas fáciles de comprender, pero que no son exactamente prácticas para el usuario. Al realizar una consulta, en lugar de mostrar un conjunto de datos, te recomendamos que extraigas algunos valores específicos. En general, se recomienda que analices las respuestas del servicio web y extraigas solo los valores que te interesen.

El esquema de análisis que uses depende del formato de salida que devuelvas, XML o JSON. Las respuestas JSON que ya tengan forma de objetos de Javascript se pueden procesar dentro de Javascript en el cliente; las respuestas XML se deben procesar con un procesador de XML y lenguaje de consulta XML para poder abordar los elementos en formato XML. En los siguientes ejemplos, usamos XPath, ya que las bibliotecas de procesamiento XML generalmente lo admiten.

Procesamiento de XML con XPath

XML es un formato de información estructurado y relativamente desarrollado que se usa para el intercambio de datos. Si bien no es tan liviano como JSON, XML ofrece compatibilidad de lenguajes más amplia y herramientas más sólidas. El código para el procesamiento de XML en Java, por ejemplo, se compila en los paquetes javax.xml.

Al procesar respuestas XML, debes usar un lenguaje de consulta adecuado para seleccionar nodos dentro del documento XML, en lugar de asumir que los elementos residen en posiciones absolutas dentro del marcado XML. XPath es una sintaxis de lenguaje para describir de forma exclusiva nodos y elementos en un documento XML. Las expresiones en XPath te permiten identificar contenido específico dentro del documento de la respuesta XML.

Expresiones XPath

Si ya estás familiarizado con XPath, te ayudará mucho en el desarrollo de un esquema de análisis sólido. Esta sección se centra en cómo se abordan con XPath los elementos dentro de un documento XML, lo que te permite abordar múltiples elementos y construir consultas complejas.

XPath usa expresiones para seleccionar elementos en un documento XML, y lo hace con una sintaxis similar a la usada para rutas de acceso a directorios. Esas expresiones identifican elementos en un árbol de documentos XML, que es un árbol jerárquico similar al de un DOM. Generalmente, las expresiones XPath son ambiciosas, lo que indica que se corresponderán con todos los nodos que coincidan con los criterios proporcionados.

Usaremos el siguiente XML abstracto para ilustrar nuestros ejemplos:

<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>

Selección de nodos en expresiones

Mediante las selecciones de XPath se seleccionan nodos. El nodo raíz abarca todo el documento. Para seleccionar este nodo, debes usar la expresión especial "/". Ten en cuenta que el nodo raíz no es el nodo de nivel superior de tu documento XML; en realidad, reside un nivel por encima del elemento de nivel superior y lo incluye.

Los nodos de los elementos representan los diferentes elementos del árbol de documentos XML. Un elemento <WebServiceResponse>, por ejemplo, representa el elemento de nivel superior devuelto en nuestro ejemplo de servicio más arriba. Puedes seleccionar nodos individuales mediante rutas de acceso absolutas o relativas que se indican por la presencia o ausencia de un carácter delantero "/".

  • Ruta de acceso absoluta: la expresión "/WebServiceResponse/result" selecciona todos los nodos <result> que sean nodos secundarios del nodo <WebServiceResponse>. (Ten en cuenta que ambos elementos descienden del nodo raíz "/").
  • Ruta de acceso relativa a partir del contexto actual: la expresión "result" se correspondería con los elementos <result> del contexto actual. En general, no debes preocuparte por el contexto, ya que normalmente procesarás resultados de servicios web a través de una sola expresión.

Cualquiera de estas expresiones se pueden incrementar mediante la adición de una ruta de acceso comodín, que se indica con dos barras diagonales ("//"). Este comodín indica que cero o más elementos pueden coincidir en la ruta de acceso interviniente. La expresión de XPath "//formatted_address", por ejemplo, coincidirá con todos los nodos que tengan ese nombre en el documento actual. La expresión //viewport//lat coincidiría con todos los elementos <lat> que pueden rastrear <viewport> como un elemento principal.

De forma predeterminada, las expresiones Xpath coinciden con todos los elementos. Puedes restringir la expresión para que coincida con un elemento determinado al proporcionar un predicado, que debe estar contenido por corchetes ([]). Por ejemplo, la expresión de XPath "/GeocodeResponse/result[2] siempre devuelve el segundo resultado.

Tipo de expresión
Nodo raíz
Expresión de XPath:  "/"
Selección:
    <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>
    
Ruta de acceso absoluta
Expresión de XPath:  "/WebServiceResponse/result"
Selección:
    <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>
    
Ruta de acceso con comodín
Expresión de XPath:  "/WebServiceResponse//location"
Selección:
    <location>
     <lat>37.4217550</lat>
     <lng>-122.0846330</lng>
    </location>
    
Ruta de acceso con predicado
Expresión de XPath:  "/WebServiceResponse/result[2]/message"
Selección:
    <message>The secret message</message>
    
Todos los elementos secundarios directos del primero result.
Expresión de XPath:  "/WebServiceResponse/result[1]/*"
Selección:
     <type>sample</type>
     <name>Sample XML</name>
     <location>
      <lat>37.4217550</lat>
      <lng>-122.0846330</lng>
     </location>
    
El name de un result cuyo texto de type sea "sample".
Expresión de XPath:  "/WebServiceResponse/result[type/text()='sample']/name"
Selección:
    Sample XML
    

Es importante tener en cuenta que al seleccionar elementos, seleccionas nodos, no solo el texto dentro de esos objetos. En general, se recomienda que repitas todos los nodos coincidentes y extraigas el texto. También puedes hacer coincidir nodos de texto directamente; consulta Nodos de texto a continuación.

Ten en cuenta que XPath también admite nodos de atributos; no obstante, todos los servicios web de Google Maps proporcionan elementos sin atributos, por lo que no es necesario que los atributos coincidan.

Selección de texto en expresiones

El texto de un documento XML se especifica en expresiones Xpath a través de un operador de nodos de texto. El "text()" de ese operador indica la extracción de texto del nodo indicado. Por ejemplo, la expresión de XPath "//formatted_address/text()" devolverá todo el texto dentro de elementos <formatted_address>.

Tipo de expresión
Todos los nodos de tecto (incluidos espacios en blanco)
Expresión de XPath:  "//text()"
Selección:
    sample
    Sample XML

    37.4217550
    -122.0846330
    The secret message
    
Selección de texto
Expresión de XPath:  "/WebServiceRequest/result[2]/message/text()"
Selección:
    The secret message
    
Selección dependiente del contexto
Expresión de XPath:  "/WebServiceRequest/result[type/text() = 'sample']/name/text()"
Selección:
    Sample XML
    

También puedes evaluar una expresión, devolver un conjunto de nodos y luego repetir ese “conjunto de nodos” al extraer el texto de cada nodo. En el siguiente ejemplo usamos este enfoque.

Para obtener más información acerca de XPath, consulta la Especificación W3C de XPath W3C.

Evaluación de XPath en Java

Java ofrece una amplia compatibilidad para el análisis de XML con expresiones de XPath dentro del paquete javax.xml.xpath.*. Por ese motivo, el código de ejemplo en esta sección usa Java para ilustrar cómo manejar XML y analizar datos de las respuestas del servicio XML.

Para usar XPath en tu código Java, primero debes crear una instancia de un XPathFactory y llamar a newXPath() en esa fábrica para crear un objeto XPath . Ese objeto luego podrá procesar con el método evaluate() las expresiones XML y XPath que se le hayan pasado.

Al evaluar expresiones de XPath, asegúrate de repetir los “conjuntos de nodos” que se podrían devolver. Dado que esos resultados se devuelven como nodos de DOM en código Java, debes capturar esos múltiples valores en un objeto NodeList y repetir ese objeto para extraer el texto o los valores de esos nodos.

El siguiente código ilustra cómo crear un objeto XPath, asignarle XML y una expresión XPath, y evaluar la expresión para imprimir el contenido relevante.

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");
    }
  }
}

Descarga el código de js-v2-samples

Procesamiento de JSON con Javascript

JSON (Javascript Object Notation) posee una ventaja obvia por sobre XML: la respuesta es liviana. Analizar un resultado como este es intrascendente en JavaScript ya que el formato ya es un objeto JavaScript válido. Por ejemplo, para extraer el valor de las claves 'formatted_address' en un objeto de resultado JSON, simplemente accede a ellas con el siguiente código:

for (i = 0; i < myJSONResult.results.length; i++) {
  myAddress[i] = myJSONResult.results[i].formatted_address;
}

Ten en cuenta que, dado que JSON puede contener múltiples valores, si quieres capturar todos los valores posibles, lo mejor es repetir la longitud de la matriz results. No obstante, en la práctica, te recomendamos que solo devuelvas el primer resultado (results[0]).

El análisis de JSON en otros lenguajes es un poco más complicado. En el siguiente ejemplo de Python, se inicializa una solicitud del servicio web de geocodificación y se muestran todos los valores formatted_address resultantes al usuario dentro de una matriz:

import simplejson, urllib

GEOCODE_BASE_URL = 'https://maps.googleapis.com/maps/api/geocode/json'

def geocode(address, **geo_args):
    geo_args.update({
        'address': address
    })

    url = GEOCODE_BASE_URL + '?' + urllib.urlencode(geo_args)
    result = simplejson.load(urllib.urlopen(url))

    print simplejson.dumps([s['formatted_address'] for s in result['results']], indent=2)

if __name__ == '__main__':
    geocode(address="San+Francisco")

Output: [ "San Francisco, CA, USA" ]

Descarga el código de js-v2-samples

El parámetro sensor

Antes, la Google Maps API requería que incluyeras el parámetro sensor para indicar si tu aplicación usaba un sensor para determinar la ubicación del usuario. El uso de este parámetro ya no es obligatorio.

Enviar comentarios sobre...

API de Google Maps para servicio web
API de Google Maps para servicio web