5 votos

¿Existe una API pública a la que se pueda acceder con Python y que permita geocodificar los puntos de interés?

Recientemente he descargado datos de hospitales etiquetados de OpenStreetMaps.org utilizando la API. Algunos de los datos tienen una etiqueta de "nodo" a la que se atribuye una latitud y una longitud, mientras que otros datos están etiquetados en formato "camino" sin un atributo de dirección. Esto me deja con varios hospitales que sólo tienen un atributo de "nombre" y ninguna geolocalización de dirección/lat.

Me pregunto si existen APIs públicas a las que se pueda acceder usando Python y que me permitan pasar sólo un nombre de hospital como, por ejemplo, 'Hospital de la CiudadA', y obtener una dirección exacta o una latitud y longitud.

4voto

steveax Puntos 316

Tengo un script que uso para tomar una lista de direcciones y geocodificarlas a puntos en una nueva clase de característica. Sólo tienes que descargar el solicita y suministrar una url de servicio para un servicio REST de geocodificación. Sin embargo, no conozco ningún servicio de geocodificación de código abierto.

import requests
import json
import arcpy
import os
import sys
arcpy.env.overwriteOutput = True

def Message(msg):
    print str(msg)
    arcpy.AddMessage(str(msg))
    return msg

def assertJsonSuccess(data):
    import json
    obj = json.loads(data)
    if 'status' in obj and obj['status'] == "error":
        Message("Error: JSON object returns an error. " + str(obj))
        return False
    else:
        return True

def geocode_addresses(new_fc, addresses=[], serviceURL='', sr=3857):
    import json
    import requests
    """Geocodes a list of addresses using a Geocode Service and returns a
    point feature class

    Quick and dirty geocode.  There may be a limit on how many addresses can be
    used before the server rejects the requests

    Required:
    new_fc -- new point feature class for geocoded addresses
    addresses -- dictionary of address and feature name {'address' : 'feature description',..}
                 or a list of addresses ['address, city, state, zip'...]
    serviceURL -- url to a REST endpoint for a Geocoding service
    sr -- spatial reference or WKID for new feature class (should be same as geocoding service)

    Addresses can be in one of the below formats. 

    # example: pass addresses with description as dictionary
    addresses = {"400 N 1st Ave W, Hartley, IA, 51346" : "Prins Laundromat",
                 "120 S 8th Ave W, Hartley, IA, 51346" : "Hengeveld Construction",
                 "173 S Central Ave, Hartley, IA, 51346" : "Legal Eyes"}

    # example: pass addresses in as a list
    addresses = ["901 N Broadway, Saint Louis, Missouri, 63102",
                 "10701 Lambert International Blvd, Saint Louis,
                 "15193 Olive Blvd, Chesterfield, Missouri, 63017"]
    """
    # check to see if addresses are provided as dict
    hasFeature = True
    if not isinstance(addresses, dict):
        addresses = dict((a, '') for a in addresses)
        hasFeature = False

    # dictionary to store addresses and points
    addr = {}
    bad = []
    for address, name in addresses.iteritems():
        # This request only needs the single line address as text and the response formatting parameter .
        params = {'text': address, 'f': 'pjson'}
        headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}

        # Connect to service to get its current JSON definition.
        r = requests.post(serviceURL, params=params, headers=headers)
        if (r.status_code != 200):
            Message("Could not geocode address {0}.\r\nhttp status code {1}.".format(address, r.status_code))
            bad.append(address)
        else:
            if not assertJsonSuccess(r.text):
                Message('Error when reading service information:  {0}'.format(r.text))
                bad.append(address)

            #Get the first returned location
            loc = False
            candidates = r.json()
            if 'locations' in candidates:
                if candidates['locations']:
                    loc = True
                    candidate = candidates['locations'][0]
                    geo = candidate['feature']['geometry']

                    # Create (X,Y) tuple and Point objects and score
                    pt = (geo['x'], geo['y'])
                    score = candidate['feature']['attributes']['Score']
                    addr[(address, name)] = (arcpy.PointGeometry(arcpy.Point(*pt), prj), score)
            if not loc:
                Message("Could not geocode address \"{0}\"".format(address))

    # create new feature class
    f_length = min([max(len(a) for a in addresses), 255])
    if arcpy.Exists(new_fc):
        arcpy.Delete_management(new_fc)
    arcpy.CreateFeatureclass_management(os.path.dirname(new_fc),
                                        os.path.basename(new_fc),
                                        'POINT', spatial_reference=sr)
    arcpy.AddField_management(new_fc, 'Address', 'TEXT', field_length=f_length)
    arcpy.AddField_management(new_fc, 'Feature', 'TEXT', field_length=100)
    arcpy.AddField_management(new_fc, 'Score', 'SHORT')

    # insert rows 
    with arcpy.da.InsertCursor(new_fc, ['SHAPE@', 'Address', 'Feature', 'Score']) as rows:
        for attributes, pt in addr.iteritems():
            add, name = attributes
            xy, score = pt
            rows.insertRow([xy, add, name, score])
            Message('Geocoded address: {0}'.format(add))

    # Delete feature field if no feature names supplied
    if not hasFeature:
        arcpy.DeleteField_management(new_fc, ['Feature'])

    # if bad records
    if bad:
        date = time.strftime('_%m_%d_%Y')
        txt = r'C:\Users\{0}\Desktop\Geocode_fail_{1}.txt'.format(os.environ['USERNAME'], date)
        with open(txt, 'w') as f:
            f.writelines('\n'.join(bad))
        os.startfile(txt)
    return new_fc

0 votos

Este código funciona bien cuando hay una dirección definida para cada hospital. Sin embargo, sólo se trata de nombres de hospitales (por ejemplo, el Hospital Milford). ¿Hay alguna manera de geocodificar las direcciones con sólo el nombre del hospital y no una dirección real?

1 votos

Esto está muy bien, acabo de encontrar una api de python gratuita para geocodificar y geocodificar direcciones de forma inversa. Echa un vistazo a esta página, debería hacer lo que quieres. He probado con un par de nombres de hospitales y devuelve las coordenadas. Utiliza el Geocoder de google maps: code.xster.net/pygeocoder/wiki/Home

2voto

Robert Fraser Puntos 231

Mapzen tiene un servicio de geocodificación muy interesante que permite consultar el nombre del lugar para obtener una coordenada XY. ( https://mapzen.com/documentation/search/search/ ). Puedes obtener una clave de API gratuita en su página para desarrolladores que te permitirá hacer 30.000 consultas al día a 6 por segundo. ( https://mapzen.com/ )

Aquí hay una función que devolverá la ubicación XY para la consulta (con entrada de cadena). Puedes cambiarla para que devuelva la respuesta para ver todos los datos resultantes (incluyendo la dirección). Además, al final de la función path se puede eliminar el &size=1 si quiere obtener todos los resultados posibles en lugar de sólo uno.

import json
import requests
import urllib

def geocode(address):
    api = 'YOUR_API_KEY_HERE'
    addresses =address
    base = 'https://search.mapzen.com'
    path = '{}/v1/search?api_key={}&text={}&size=1'.format(base,api, urllib.quote_plus(address))
    response = requests.get(path, verify=False)
    answer = response.json()
    return answer['features'][0]['geometry']['coordinates']

Ejemplo:

 >>> geocode("golds gym, austin, tx")
[-97.83996, 30.223798]

EDIT: Acabo de probar tu ejemplo del "Hospital de Milford". (en XY, no en lat/long)

>>> geocode("Milford Hospital")
[-0.625876, 51.167924]

ACTUALIZACIÓN: Lamentablemente Mapzen ya no está activo, aunque el código está en GitHub por si quieres alojar el tuyo propio. Para la función de arriba, usted tendría que reemplazar el base y path para reflejar la API de geocodificación que quieras utilizar (como la de google), formatea la URL adecuadamente, y establece tu valor de retorno para que sean los valores X e Y devueltos en el response.json().

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X