Géocodage Comment lier 250 mille adresses à des coordonnées en 10 minutes?



Bonjour, Habr!

Dans cet article, je voudrais partager mon expérience dans la résolution d'un petit problÚme avec un grand nombre d'adresses. Si vous avez déjà travaillé avec l'API de géocodage ou utilisé des outils en ligne, je pense que vous partagez ma douleur d'attendre le résultat pendant plusieurs heures, voire plus.

Il ne s'agit pas d'algorithmes d'optimisation complexes, mais de l'utilisation d'un service de géocodage de paquets qui prend une liste d'adresses en entrée et renvoie un fichier avec les résultats. Cela peut réduire le temps de traitement de quelques heures à quelques minutes.

Tout d'abord:


Contexte


La tùche est arrivée - "Lier aux coordonnées de 24 mille adresses." Seules deux solutions au problÚme se sont produites:

  1. Application Web de géocodage, utilisée à l'université;
  2. Écrivez un script basĂ© sur l'API REST du gĂ©ocodeur.

Dans le premier cas, il s'est avéré que l'application Web se bloquait aprÚs avoir traité des milliers d'adresses. La distribution d'un ensemble de données entre collÚgues est une idée qui a été immédiatement abandonnée.

Par conséquent, vous devez utiliser l'API REST du géocodeur pour écrire votre propre script, avec les résultats enregistrés (ce n'est pas une maniÚre complÚtement légale et vous devez lire les conditions d'utilisation du service). Un nouveau problÚme survient - c'est une chose lorsque nous utilisons la recherche d'adresses dans l'application et obtenons immédiatement le résultat, mais lorsque la tùche consiste à traiter plus de dix mille adresses avec conservation, le travail du script est considérablement retardé. Vous pouvez attendre une heure ou deux, mais un million d'adresses devront géocoder «un temps immense», vous devez donc chercher une autre solution et c'est le cas!

Les grands fournisseurs de services de géolocalisation, en plus du service de géocodage habituel, proposent un géocodeur de paquets (Batch Geocoder), juste pour traiter un grand nombre d'adresses en une seule demande.

GĂ©ocodage par lots


Le nom du service parle de lui-mĂȘme - nous avons un package (par exemple, un fichier csv avec une liste d'adresses sous la forme d'un tableau), que nous tĂ©lĂ©chargeons sur le serveur, et il fait tout le travail pour nous.

Le processus ressemble Ă  ceci:

  1. Préparer l'ensemble de données pour que le service l'accepte sans erreur;
  2. Définition des paramÚtres du résultat du travail (sélection des colonnes, séparateur ...);
  3. Téléchargez un fichier dans le cloud;
  4. En attente de fin de traitement;
  5. Téléchargez le fichier fini.

GrĂące Ă  la puissance du cloud computing, ce qui se fait avec un script auto-Ă©crit en 1 heure se fait en 1 minute.

L'Ă©tape suivante consiste Ă  choisir l'entreprise avec les conditions d'utilisation les plus fidĂšles du gĂ©ocodeur de paquets. Tout d'abord, tout le monde n'a pas un tel service, d'autres vous permettent de tester le service avec une sĂ©rieuse limitation. De plus, si vous avez de trĂšs gros volumes, vous devez faire attention au coĂ»t des transactions supplĂ©mentaires, au cas oĂč la limite du forfait gratuit serait dĂ©passĂ©e.

Choisir un fournisseur de services de géocodage par lots


Sur le marché mondial des services de géolocalisation, les positions de leader sont occupées par:

  • Google Maps
  • ICI Technologies;
  • MapBox
  • TomTom
  • ESRI.

Bien sûr, il ne faut pas oublier Yandex Technologies, qui a une position assez forte en Russie.

J'ai pris les paramĂštres suivants comme base pour choisir un fournisseur:

  • Le nombre de demandes par mois auprĂšs du service de gĂ©ocodage est gratuit;
  • Limite du nombre de transactions par jour;
  • DisponibilitĂ© du service de gĂ©ocodage par lots;
  • PossibilitĂ© d'utiliser un gĂ©ocodeur de paquets dans un plan gratuit.

Chaque entreprise a son propre modĂšle de monĂ©tisation. Selon le projet, l'un ou l'autre modĂšle peut jouer entre les mains ou vice versa ĂȘtre une limitation importante.

Google Maps


Pour commencer avec les services gĂ©ographiques de Google, la premiĂšre chose que vous devez faire est d'ajouter des informations de carte de crĂ©dit Ă  votre compte. Une limite mensuelle de 200 dollars virtuels, vient ensuite le paiement de transactions supplĂ©mentaires Ă  partir de la carte liĂ©e. Dans cette limite, vous pouvez utiliser diffĂ©rents services, mais chaque transaction est considĂ©rĂ©e diffĂ©remment. Par exemple, mille demandes de gĂ©ocodage coĂ»teront 5 $, mais le service de crĂ©ation d'itinĂ©raire est deux fois plus cher. Plus de dĂ©tails peuvent ĂȘtre trouvĂ©s sur le site, nous ne sommes intĂ©ressĂ©s que par le service de gĂ©ocodage.

Si 200 $ par mois, il est facile de calculer le nombre gratuit de transactions - 40 000 (service de gĂ©ocodage). Il n'y a pas de gĂ©ocodeur de paquets parmi les services. Cela signifie que vous devez Ă©crire votre propre script et le rĂ©sultat sera d'environ 1 adresse par seconde, soit six heures pour 24 000 adresses. Pour accĂ©lĂ©rer le processus, vous pouvez essayer d'exĂ©cuter le script sur la plate-forme des API Google Cloud, mais j'ai dĂ©cidĂ© de rechercher des solutions alternatives. Il n'y a aucune restriction sur le nombre de transactions par jour, donc les quarante mille peuvent ĂȘtre dĂ©pensĂ©s Ă  la fois.

ICI Technologies


Dans le passé, Nokia Maps, et dans un passé encore plus profond, Navteq, fournit gratuitement 250 000 transactions par mois. Comme pour Google Maps, ce numéro s'applique à tous les services et chacun est considéré différemment. Lorsque vous utilisez le forfait gratuit, vous n'avez pas besoin de joindre une carte bancaire. Si vous dépassez la limite, alors pour chaque millier de transactions supplémentaires, vous devez payer 1 $.

Il est important d'avoir un gĂ©ocodeur de paquets en tant que service distinct, qui est inclus dans le plan gratuit. Les transactions y sont prises en compte selon le mĂȘme modĂšle que dans l'habituel, c'est-Ă -dire qu'un gĂ©ocodeur de paquets percevra chaque adresse d'un fichier en une seule transaction.
Par le titre de l'article, il est clair que j'ai utilisé le géocodeur de paquets ICI, car vous pouvez dépenser toutes les transactions sur le géocodeur et effectuer 250 000 opérations de géocodage par mois. Mais ce n'est pas la seule option, alors nous regardons ce que les autres entreprises ont.

Mapbox


Lorsque vous utilisez le gĂ©ocodeur MapBox, 100 000 transactions par mois sont disponibles. L'entreprise adhĂšre au mĂȘme modĂšle de monĂ©tisation avec paiement des transactions supplĂ©mentaires. Seulement, il existe une option intĂ©ressante pour les «grossistes» - plus vous avez de transactions, moins elles coĂ»tent (bien sĂ»r, il y a une limite de rĂ©duction de prix). Par exemple, de 100 000 Ă  500 000, un millier de demandes supplĂ©mentaires coĂ»tera 0,75 $, de 500 000 Ă  1 million - 0,60 $, etc., pour plus de dĂ©tails, consultez le site Web. Malheureusement, le gĂ©ocodeur par lots n'est disponible que sur un compte payant.

Tomtom


La plateforme permet d'effectuer 2500 transactions par jour, environ 75 000 par mois. Pendant les tests et le développement, la limite quotidienne ne semble pas trÚs attrayante par rapport aux concurrents, mais le paiement des transactions supplémentaires est le plus flexible. Il existe 8 options de paiement pour un millier de demandes supplémentaires et le prix est réduit de 0,5 $ à 0,42 $.

Parmi les services, il y a un gĂ©ocodeur par lots avec la capacitĂ© de traiter jusqu'Ă  10 000 adresses par demande (cependant, la limite quotidienne doit ĂȘtre prise en compte).

Yandex Technologies


Un modÚle avec une limite de transaction quotidienne pour Yandex, mais plus fidÚle 25 000 demandes. Si vous multipliez ce nombre par le nombre de jours dans un mois, vous obtenez un chiffre impressionnant de 750 mille. Le site présente les prix d'un millier de transactions supplémentaires en roubles allant de 120 roubles. jusqu'à 11 roubles

Le géocodeur de paquets en tant que service n'est pas présenté, donc pour parvenir à une sorte d'optimisation, il échouera.

ESRI


Un plan gratuit trÚs tentant avec 1 million de transactions par mois. L'entreprise facture également 50 crédits à chaque compte (l'équivalent approximatif de 5 $). Il convient de noter qu'il s'agit du plan le plus fidÚle pour l'utilisation des services de géolocalisation. Il existe également un service de géocodage par lots, mais vous ne pouvez l'utiliser que si vous disposez d'un compte d'entreprise sur la plateforme ArcGIS Online.

Que choisir Ă  la fin?


La façon la plus simple est de faire un choix en compilant une petite table:



En conséquence, mon choix est tombé ICI car c'est la meilleure option pour résoudre mon problÚme. Bien sûr, je suis loin d'une analyse complÚte, idéalement, vous devez exécuter votre ensemble de données à travers tous les géocodeurs pour évaluer la qualité. De plus, si vous avez plusieurs millions d'adresses, vous devriez penser à un forfait payant et vous devez alors prendre en compte le coût de l'ajout. transactions.

Le but de l'article n'est pas de comparer les entreprises, mais de résoudre le problÚme d'optimisation du géocodage d'un grand volume d'adresses. Je viens de montrer mes pensées lors du choix d'un fournisseur de services.

Guide de service Python


Vous devez d'abord créer un compte sur le portail pour les développeurs et générer une clé API REST dans la section projet.

Vous pouvez désormais travailler avec la plateforme. Je ne décrirai qu'une partie des fonctionnalités du géocodeur de paquets ICI: chargement des données, vérification de l'état, sauvegarde des résultats.

Commençons donc par importer les bibliothÚques nécessaires:

import requests
import json
import time
import zipfile
import io
from bs4 import BeautifulSoup

De plus, si aucune erreur ne s'est produite, créez une classe:

class Batch:

    SERVICE_URL = "https://batch.geocoder.ls.hereapi.com/6.2/jobs"
    jobId = None

    def __init__(self, apikey="your_api_key"):
        self.apikey = apikey

En d'autres termes, lors de l'initialisation, la classe doit transmettre sa propre clé pour l'API REST.
La variable SERVICE_URL est l'URL de base pour travailler avec le service de géocodage par lots.
Et dans jobId l'identifiant du travail en cours du géocodeur sera stocké.

Un point important est la structure de données correcte sur demande. Le fichier doit contenir deux colonnes obligatoires: recId et searchText. Sinon, le service renverra une réponse contenant des informations sur l'erreur de téléchargement.

Voici un exemple de jeu de données:

   recId; searchText
   1; -, . , 6
   2; ,  1,  -., 72
   3; 425 W Randolph St Chicago IL 60606
   4; , DJ106 20-30, Sibiu 557260
   5; 200 S Mathilda Ave Sunnyvale CA 94086
  

Fonction de téléchargement d'un fichier sur le cloud:

def start(self, filename, indelim=";", outdelim=";"):
        
        file = open(filename, 'rb')

        params = {
            "action": "run",
            "apiKey": self.apikey,
            "politicalview":"RUS",
            "gen": 9,
            "maxresults": "1",
            "header": "true",
            "indelim": indelim,
            "outdelim": outdelim,
            "outcols": "displayLatitude,displayLongitude,locationLabel,houseNumber,street,district,city,postalCode,county,state,country",
            "outputcombined": "true",
        }

        response = requests.post(self.SERVICE_URL, params=params, data=file)
        self.__stats (response)
        file.close()


Tout est assez simple, ouvrez un fichier avec une liste d'adresses Ă  lire, formez un dictionnaire des paramĂštres de requĂȘte GET. Certains paramĂštres mĂ©ritent d'ĂȘtre expliquĂ©s:

  • «Action»: «exĂ©cuter» - dĂ©but du traitement de l'adresse;
  • “politicalView”: “RUS” – . ( );
  • “gen”: 9 – ( );
  • “maxresults”: 1 – ;
  • “header”: true – ;
  • “indelim”: “;” – , ;
  • “outdelim”: “;” – ;
  • “outcols”: “” – , ;
  • “outcombined”: true – .

Ensuite, envoyez simplement une demande à l'aide de la bibliothÚque de demandes et affichez les statistiques. Bien sûr, vous devez fermer le fichier à la fin de la fonction. La fonction __stats analyse la réponse du serveur, qui contient l'ID du travail en cours d'exécution, et affiche également des informations générales sur l'opération.

L'Ă©tape suivante consiste Ă  vĂ©rifier l'Ă©tat du travail. La requĂȘte est formĂ©e de façon similaire, il suffit de transfĂ©rer l'opĂ©ration Id. Le paramĂštre d'action doit contenir la valeur «état». La fonction __stats affiche des statistiques complĂštes sur la console pour estimer l'heure d'arrĂȘt du gĂ©ocodeur.

    def status (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        statusUrl = self.SERVICE_URL + "/" + self.jobId
        
        params = {
            "action": "status",
            "apiKey": self.apikey,
        }
        
        response = requests.get(statusUrl, params=params)
        self.__stats (response)

L'une des fonctions les plus importantes consiste à enregistrer le résultat. Pour plus de commodité, il est préférable de décompresser immédiatement le fichier provenant du serveur. La demande de sauvegarde du fichier est identique à la vérification de l'état, il suffit d'ajouter / résultat à la fin.

    def result (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        print("Requesting result data ...")
        
        resultUrl = self.SERVICE_URL + "/" + self.jobId + "/result"
        
        params = {
            "apiKey": self.apikey
        }
        
        response = requests.get(resultUrl, params=params, stream=True)
        
        if (response.ok):    
            zipResult = zipfile.ZipFile(io.BytesIO(response.content))
            zipResult.extractall()
            print("File saved successfully")
        
        else:
            print("Error")
            print(response.text)

La fonction finale pour analyser la réponse du service. Sa tùche consiste également à enregistrer l'identifiant de la tùche de géocodage en cours:

    def __stats (self, response):
        if (response.ok):
            parsedXMLResponse = BeautifulSoup(response.text, "lxml")

            self.jobId = parsedXMLResponse.find('requestid').get_text()
            
            for stat in parsedXMLResponse.find('response').findChildren():
                if(len(stat.findChildren()) == 0):
                    print("{name}: {data}".format(name=stat.name, data=stat.get_text()))

        else:
            print(response.text)

Pour le tester, il suffit d'exécuter l'interpréteur Python dans le dossier de script. La classe Batch se trouve dans le fichier geocoder.py :

>>> from geocoder import Batch
>>> service = Batch(apikey="   REST API")
>>> service.start("big_data_addresses.csv", indelim=";", outdelim=";")

requestid: "  Id "
status: accepted
totalcount: 0
validcount: 0
invalidcount: 0
processedcount: 0
pendingcount: 0
successcount: 0
errorcount: 0


Un excellent travail a commencé. Vérifiez l'état:

>>> service.status()

requestid: "  Id "
status: completed
jobstarted: 2020-04-27T10:09:58.000Z
jobfinished: 2020-04-27T10:17:18.000Z
totalcount: 249999
validcount: 249999
invalidcount: 0
processedcount: 249999
pendingcount: 0
successcount: 249978
errorcount: 21

Nous voyons que le traitement de l'ensemble de données est terminé. En seulement sept minutes, il a été possible de procoder les 250 000 adresses (hors erreurs - errorcount). Reste à sauvegarder les résultats:

>>> service.result()
Requesting result data ...
File saved successfully

Description complĂšte de la classe de lot


Je pense que cela ne fait pas de mal d’ajouter complùtement le script:

import requests
import json
import time
import zipfile
import io
from bs4 import BeautifulSoup

class Batch:

    SERVICE_URL = "https://batch.geocoder.ls.hereapi.com/6.2/jobs"
    jobId = None

    def __init__(self, apikey="   REST API "):
        self.apikey = apikey
        
            
    def start(self, filename, indelim=";", outdelim=";"):
        
        file = open(filename, 'rb')

        params = {
            "action": "run",
            "apiKey": self.apikey,
            "politicalview":"RUS",
            "gen": 9,
            "maxresults": "1",
            "header": "true",
            "indelim": indelim,
            "outdelim": outdelim,
            "outcols": "displayLatitude,displayLongitude,locationLabel,houseNumber,street,district,city,postalCode,county,state,country",
            "outputcombined": "true",
        }

        response = requests.post(self.SERVICE_URL, params=params, data=file)
        self.__stats (response)
        file.close()
    

    def status (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        statusUrl = self.SERVICE_URL + "/" + self.jobId
        
        params = {
            "action": "status",
            "apiKey": self.apikey,
        }
        
        response = requests.get(statusUrl, params=params)
        self.__stats (response)
        

    def result (self, jobId = None):

        if jobId is not None:
            self.jobId = jobId
        
        print("Requesting result data ...")
        
        resultUrl = self.SERVICE_URL + "/" + self.jobId + "/result"
        
        params = {
            "apiKey": self.apikey
        }
        
        response = requests.get(resultUrl, params=params, stream=True)
        
        if (response.ok):    
            zipResult = zipfile.ZipFile(io.BytesIO(response.content))
            zipResult.extractall()
            print("File saved successfully")
        
        else:
            print("Error")
            print(response.text)
    

    
    def __stats (self, response):
        if (response.ok):
            parsedXMLResponse = BeautifulSoup(response.text, "lxml")

            self.jobId = parsedXMLResponse.find('requestid').get_text()
            
            for stat in parsedXMLResponse.find('response').findChildren():
                if(len(stat.findChildren()) == 0):
                    print("{name}: {data}".format(name=stat.name, data=stat.get_text()))

        else:
            print(response.text)

L'analyse des résultats


En conséquence, je suis passé lentement des applications en ligne aux services de géocodage par lots. Le choix d'un géoservice dépend entiÚrement des tùches qui vous attendent. Je reçois réguliÚrement des demandes de traitement d'un grand nombre d'adresses et l'approche décrite dans l'article a permis de réduire considérablement le temps.

J'espÚre que cet article vous sera utile et bien sûr je suis ouvert aux commentaires et ajouts!

All Articles