الترميز الجغرافي كيفية ربط 250 ألف عنوان للإحداثيات في 10 دقائق؟



مرحبا يا هابر!

في هذه المقالة ، أود أن أشارك تجربتي في حل مشكلة صغيرة مع عدد كبير من العناوين. إذا كنت قد عملت في أي وقت مضى مع واجهة برمجة التطبيقات للتكويد الجغرافي أو استخدمت أدوات عبر الإنترنت ، فأعتقد أنك تشارك الألم في انتظار النتيجة لعدة ساعات ، أو حتى أكثر.

لا يتعلق هذا بخوارزميات التحسين المعقدة ، ولكن حول استخدام خدمة ترميز جغرافي للحزمة تأخذ قائمة العناوين كمدخلات وترجع ملفًا مع النتائج. يمكن أن يقلل ذلك من وقت المعالجة من بضع ساعات إلى دقائق.

اهم الاشياء اولا:


خلفية


وصلت المهمة - "ربط إحداثيات 24 ألف عنوان". حدث حلان فقط للمشكلة:

  1. تطبيق ويب للترميز الجغرافي ، والذي تم استخدامه في الجامعة ؛
  2. اكتب نصًا برمجيًا استنادًا إلى REST API للمكوّن الجغرافي.

في الحالة الأولى ، اتضح أن تطبيق الويب يتعطل بعد معالجة آلاف العناوين. توزيع مجموعة بيانات بين الزملاء هي فكرة تم التخلي عنها على الفور.

لذلك ، تحتاج إلى استخدام واجهة برمجة تطبيقات REST الخاصة بالمشفر الجغرافي لكتابة البرنامج النصي الخاص بك ، مع حفظ النتائج (هذه ليست طريقة قانونية تمامًا وتحتاج إلى قراءة شروط استخدام الخدمة). تنشأ مشكلة جديدة - إنه شيء واحد عندما نستخدم البحث عن العنوان في التطبيق ونحصل على النتيجة على الفور ، ولكن عندما تكون المهمة هي معالجة أكثر من عشرة آلاف عنوان مع الحفظ ، يتأخر عمل البرنامج النصي إلى حد كبير. يمكنك الانتظار لمدة ساعة أو ساعتين ، ولكن سيتعين على مليون عنوان ترميز جغرافي لـ "وقت هائل" ، لذا عليك البحث عن حل آخر وهو!

يقدم كبار موفري خدمات تحديد الموقع الجغرافي ، بالإضافة إلى خدمة الترميز الجغرافي المعتادة ، حزمة ترميز جغرافي (Batch Geocoder) ، فقط لمعالجة عدد كبير من العناوين في طلب واحد.

الدفعة الترميز الجغرافي


اسم الخدمة يتحدث عن نفسه - لدينا حزمة (على سبيل المثال ، ملف csv مع قائمة عناوين في شكل جدول) ، والتي نقوم بتحميلها إلى الخادم ، وتقوم بكل العمل نيابة عنا.

تبدو العملية كما يلي:

  1. تحضير مجموعة البيانات بحيث تقبلها الخدمة دون أخطاء ؛
  2. تحديد معلمات نتيجة العمل (اختيار الأعمدة ، الفاصل ...) ؛
  3. تحميل ملف إلى السحابة ؛
  4. في انتظار اكتمال المعالجة ؛
  5. قم بتنزيل الملف النهائي.

بفضل قوة الحوسبة السحابية ، يكتمل ما يتم عمله باستخدام نص مكتوب ذاتيًا في ساعة واحدة في دقيقة واحدة.

والخطوة التالية هي اختيار الشركة ذات شروط الاستخدام الأكثر ولاءً للحزمة الجغرافية. أولاً ، ليس لدى الجميع مثل هذه الخدمة ، ويسمح لك الآخرون باختبار الخدمة بحدود خطيرة. أيضًا ، إذا كان لديك كميات كبيرة جدًا ، فيجب الانتباه إلى تكلفة المعاملات الإضافية ، في حالة تجاوز حد الحزمة المجانية.

اختيار موفر خدمة التكويد الجغرافي للدفعة


في السوق العالمية لخدمات تحديد الموقع الجغرافي ، تحتل المناصب القيادية ما يلي:

  • خرائط جوجل
  • تقنيات هنا ؛
  • MapBox
  • TomTom
  • ESRI.

بالطبع ، يجب ألا تنسى Yandex Technologies ، التي تتمتع بموقع قوي إلى حد ما في روسيا.

أخذت المعلمات التالية كأساس لاختيار مزود:

  • عدد طلبات خدمة الترميز الجغرافي في الشهر مجانية ؛
  • الحد من عدد المعاملات في اليوم الواحد ؛
  • توفر خدمة التشفير الجغرافي الدفعي ؛
  • القدرة على استخدام حزمة جودر في خطة مجانية.

لكل شركة نموذجها الخاص بتحقيق الدخل. اعتمادًا على المشروع ، يمكن أن يلعب نموذج أو آخر في متناول اليد أو العكس بالعكس يكون قيدًا كبيرًا.

خرائط جوجل


لبدء استخدام خدمات Google الجغرافية ، أول شيء عليك القيام به هو إضافة معلومات بطاقة الائتمان إلى حسابك. حد شهري 200 دولار افتراضي ، ثم يأتي دفع معاملات إضافية من البطاقة المرتبطة. ضمن هذا الحد ، يمكنك استخدام خدمات مختلفة ، ولكن تعتبر كل معاملة مختلفة. على سبيل المثال ، سيكلف ألف طلب ترميز جغرافي 5 دولارات ، ولكن خدمة بناء المسار باهظة الثمن. يمكن العثور على مزيد من التفاصيل على الموقع ، نحن مهتمون فقط بخدمة الترميز الجغرافي.

إذا كان 200 دولارًا أمريكيًا في الشهر ، فمن السهل حساب العدد المجاني للمعاملات - 40000 (خدمة الترميز الجغرافي). لا يوجد حزمة جودر بين الخدمات. هذا يعني أنه يجب عليك كتابة البرنامج النصي الخاص بك وستكون النتيجة حوالي 1 عنوان في الثانية ، وهي ست ساعات لـ 24 ألف عنوان. لتسريع العملية ، يمكنك تجربة تشغيل النص البرمجي على منصة Google Cloud APIs ، لكنني قررت البحث عن حلول بديلة. لا توجد قيود على عدد المعاملات في اليوم ، لذلك يمكن إنفاق أربعين ألفًا في المرة الواحدة.

تقنيات هنا


في الماضي ، توفر خرائط نوكيا ، وفي الماضي الأعمق ، Navteq ، 250.000 معاملة شهريًا مجانًا. على غرار خرائط Google ، ينطبق هذا الرقم على جميع الخدمات ويعتبر كل منها مختلفًا. عند استخدام الباقة المجانية ، لا تحتاج إلى إرفاق بطاقة مصرفية. إذا تجاوزت الحد ، فيجب عليك دفع دولار واحد لكل ألف معاملة إضافية.

من المهم أن يكون هناك حزمة جودر كخدمة منفصلة ، والتي يتم تضمينها في الخطة المجانية. يتم أخذ المعاملات الواردة فيه في الاعتبار وفقًا للنموذج نفسه كما هو الحال في النموذج المعتاد ، أي أنه في كل عنوان في الملف ، سوف يدرك الحزم الجغرافي في حزمة واحدة.
من خلال عنوان المقال ، من الواضح أنني استخدمت برنامج الترميز الجغرافي لمجموعة HERE ، حيث يمكنك إنفاق جميع المعاملات على التشفير الجغرافي وتنفيذ 250،000 عملية ترميز جغرافي شهريًا. لكن هذا ليس الخيار الوحيد ، لذلك ننظر إلى ما لدى الشركات الأخرى.

Mapbox


عند استخدام المبرمج الجغرافي MapBox ، تتوفر 100 ألف معاملة شهريًا. تلتزم الشركة بنفس نموذج تحقيق الدخل مع الدفع مقابل المعاملات الإضافية. فقط هناك خيار مثير للاهتمام "لبائعي الجملة" - كلما زاد عدد المعاملات التي لديك ، كلما قل تكلفتها (بالطبع هناك حد لتخفيض السعر). على سبيل المثال ، من 100 ألف إلى 500 ألف ، سيكلف ألف طلب إضافي 0.75 دولار ، من 500 ألف إلى مليون - 0.60 دولار ، وما إلى ذلك ، لمزيد من التفاصيل ، راجع موقع الويب. لسوء الحظ ، لا يتوفر برنامج التشفير الجغرافي للدفعات إلا في حساب مدفوع.

الطبل


تتيح المنصة تنفيذ 2500 معاملة في اليوم ، حوالي 75000 في الشهر. أثناء الاختبار والتطوير ، لا يبدو الحد اليومي جذابًا جدًا مقارنة بالمنافسين ، ولكن الدفع مقابل المعاملات الإضافية هو الأكثر مرونة. هناك 8 خيارات دفع لألف طلب إضافي ويتم تخفيض السعر من 0.5 دولار إلى 0.42 دولار.

من بين الخدمات هناك جودر دفعي مع القدرة على معالجة ما يصل إلى 10 آلاف عنوان لكل طلب (ومع ذلك ، يجب أن يؤخذ الحد اليومي في الاعتبار).

ياندكس تكنولوجيز


نموذج بحد المعاملات اليومي لـ Yandex ، ولكن أكثر 25 ألف طلب مخلص. إذا قمت بضرب هذا الرقم في عدد الأيام في الشهر ، فستحصل على رقم مثير للإعجاب قدره 750 ألفًا. يقدم الموقع أسعار ألف معاملة إضافية بالروبل تتراوح بين 120 روبل. ما يصل إلى 11 روبل

لم يتم تقديم حزمة التشفير الجغرافي كخدمة ، لذلك سيفشل تحقيق نوع من التحسين.

ESRI


خطة مجانية مغرية للغاية مع مليون معاملة شهريًا. كما تفرض الشركة 50 رصيدًا لكل حساب (ما يعادل تقريبًا 5 دولارات). ومن الجدير بالذكر أن هذه هي الخطة الأكثر ولاء لاستخدام خدمات تحديد الموقع الجغرافي. هناك أيضًا خدمة ترميز جغرافي مجمعة ، ولكن لا يمكنك استخدامها إلا إذا كان لديك حساب شركة على منصة ArcGIS Online.

ماذا تختار في النهاية؟


أسهل طريقة هي الاختيار من خلال تجميع جدول صغير:



ونتيجة لذلك ، وقع اختياري على هنا لأنه الخيار الأفضل لحل مشكلتي. بالطبع ، لم أذهب بعيدًا عن التحليل الكامل ، من الناحية المثالية تحتاج إلى تشغيل مجموعة بياناتك عبر جميع المبرمجين الجغرافيين لتقييم الجودة. بالإضافة إلى ذلك ، إذا كان لديك عدة ملايين من العناوين ، يجب أن تفكر في حزمة مدفوعة ثم تحتاج إلى مراعاة تكلفة الإضافة. المعاملات.

ليس الغرض من المقالة مقارنة الشركات ، ولكن لحل مشكلة تحسين الترميز الجغرافي لعدد كبير من العناوين. لقد عرضت أفكاري للتو عند اختيار مقدم الخدمة.

دليل خدمة Python


تحتاج أولاً إلى إنشاء حساب على البوابة الإلكترونية للمطورين وإنشاء REST API KEY في قسم المشروع.

الآن يمكنك العمل مع النظام الأساسي. سوف أصف جزءًا فقط من الوظائف التي يمتلكها الحزم الجغرافي للحزمة هنا: تحميل البيانات ، فحص الحالة ، حفظ النتائج.

لذا ، فلنبدأ باستيراد المكتبات الضرورية:

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="your_api_key"):
        self.apikey = apikey

أي أنه أثناء التهيئة ، يجب على الفصل تمرير مفتاحه الخاص بـ REST API.
متغير SERVICE_URL هو عنوان URL الأساسي للعمل مع خدمة التكويد الجغرافي للدفعة.
وفي jobId ، سيتم تخزين معرّف العمل الحالي للمكوّن الجغرافي.

نقطة مهمة هي بنية البيانات الصحيحة عند الطلب. يجب أن يحتوي الملف على عمودين مطلوبين: recId و searchText. بخلاف ذلك ، ستقوم الخدمة بإرجاع استجابة تحتوي على معلومات حول خطأ التنزيل.

فيما يلي مثال لمجموعة البيانات:

   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
  

وظيفة تحميل ملف إلى السحابة:

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


كل شيء بسيط للغاية ، افتح ملفًا يحتوي على قائمة عناوين للقراءة ، وقم بتشكيل قاموس لمعلمات طلب GET. بعض المعلمات تستحق التوضيح:

  • "الإجراء": "تشغيل" - بدء معالجة العنوان ؛
  • “politicalView”: “RUS” – . ( );
  • “gen”: 9 – ( );
  • “maxresults”: 1 – ;
  • “header”: true – ;
  • “indelim”: “;” – , ;
  • “outdelim”: “;” – ;
  • “outcols”: “” – , ;
  • “outcombined”: true – .

بعد ذلك ، ما عليك سوى إرسال طلب باستخدام مكتبة الطلبات وعرض الإحصائيات. بالطبع ، تحتاج إلى إغلاق الملف في نهاية الوظيفة. تقوم دالة __stats بتحليل استجابة الخادم ، والتي تحتوي على معرف العمل الجاري ، كما تعرض معلومات عامة حول العملية.

الخطوة التالية هي التحقق من حالة العمل. يتم تشكيل الطلب بطريقة مماثلة ، فقط من الضروري نقل معرف العملية. يجب أن تحتوي معلمة الإجراء على القيمة "الحالة". تعرض وظيفة __stats إحصائيات كاملة لوحدة التحكم لتقدير وقت إيقاف تشغيل الترميز الجغرافي.

    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)

لاختبار ذلك ، قم بتشغيل مترجم Python في مجلد البرنامج النصي. فئة Batch موجودة في ملف 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


بدأ عمل عظيم. تحقق من الحالة:

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

نرى أن معالجة مجموعة البيانات قد اكتملت. في سبع دقائق فقط ، كان من الممكن توفير 250 ألف عنوان (باستثناء الأخطاء - عدد الأخطاء). يبقى حفظ النتائج:

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

وصف فئة الدفعة الكاملة


أعتقد أنه لا يؤذي إضافة النص بالكامل:

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)

تحليل النتائج


ونتيجة لذلك ، انتقلت من العمل عبر الإنترنت ببطء إلى خدمات الترميز الجغرافي المجمعة. يعتمد اختيار مزود الخدمة الجغرافية كليًا على المهام التي تواجهك. أتلقى بانتظام طلبات لمعالجة عدد كبير من العناوين وساعد النهج الموصوف في المقالة على تقليل الوقت بشكل كبير.

آمل أن تكون هذه المقالة مفيدة وبالطبع أنا منفتح على التعليقات والإضافات!

All Articles