Google Maps Platform और Google Cloud की मदद से, फ़ुल स्टैक स्टोर लोकेटर बनाना

1. परिचय

खास जानकारी

मान लें कि आपको मैप पर कई जगहें दिखानी हैं. साथ ही, आपको यह भी पक्का करना है कि उपयोगकर्ता इन जगहों को देख पाएं और यह पहचान पाएं कि उन्हें किस जगह पर जाना है. इसके सामान्य उदाहरणों में ये शामिल हैं:

  • खुदरा दुकानदार की वेबसाइट पर स्टोर लोकेटर
  • आने वाले चुनाव के लिए पोलिंग बूथ की लोकेशन का मैप
  • खास जगहों की डायरेक्ट्री, जैसे कि बैटरी रीसाइकल करने के लिए बने कंटेनर

आपको क्या बनाना है

इस कोडलैब में, आपको एक लोकेटर बनाना होगा. यह लोकेटर, खास जगहों के लाइव डेटा फ़ीड से जानकारी लेता है. साथ ही, यह उपयोगकर्ता को उसके शुरुआती पॉइंट के सबसे नज़दीकी जगह ढूंढने में मदद करता है. यह फ़ुल-स्टैक लोकेटर, सामान्य स्टोर लोकेटर की तुलना में ज़्यादा जगहों की जानकारी दिखा सकता है. सामान्य स्टोर लोकेटर, सिर्फ़ 25 या इससे कम स्टोर की लोकेशन दिखा सकता है.

2ece59c64c06e9da.png

आपको क्या सीखने को मिलेगा

इस कोडलैब में, ओपन डेटा सेट का इस्तेमाल किया गया है. इससे स्टोर की कई जगहों के बारे में पहले से भरे गए मेटाडेटा को सिम्युलेट किया जा सकता है, ताकि आप मुख्य तकनीकी कॉन्सेप्ट सीखने पर ध्यान दे सकें.

  • Maps JavaScript API: पसंद के मुताबिक बनाए गए वेब मैप पर कई जगहों की जानकारी दिखाना
  • GeoJSON: यह एक ऐसा फ़ॉर्मैट है जो जगहों के बारे में मेटाडेटा सेव करता है
  • जगह की जानकारी अपने-आप भरने की सुविधा: इससे उपयोगकर्ताओं को शुरुआती जगहों की जानकारी जल्दी और ज़्यादा सटीक तरीके से देने में मदद मिलती है
  • Go: यह प्रोग्रामिंग भाषा है. इसका इस्तेमाल ऐप्लिकेशन के बैक-एंड को डेवलप करने के लिए किया जाता है. बैकएंड, डेटाबेस के साथ इंटरैक्ट करेगा और क्वेरी के नतीजों को फ़ॉर्मैट किए गए JSON में फ़्रंट-एंड को वापस भेजेगा.
  • App Engine: वेब ऐप्लिकेशन को होस्ट करने के लिए

ज़रूरी शर्तें

  • एचटीएमएल और JavaScript की बुनियादी जानकारी
  • Google खाता

2. सेट अप करें

इस कोडलैब के लिए, यहां दिए गए तीसरे चरण में Maps JavaScript API, Places API, और Distance Matrix API चालू करें.

Google Maps Platform का इस्तेमाल शुरू करना

अगर आपने पहले कभी Google Maps Platform का इस्तेमाल नहीं किया है, तो Google Maps Platform का इस्तेमाल शुरू करने से जुड़ी गाइड पढ़ें या Google Maps Platform का इस्तेमाल शुरू करने से जुड़ी प्लेलिस्ट देखें. इसके बाद, यहां दिया गया तरीका अपनाएं:

  1. बिलिंग खाता बनाएं.
  2. प्रोजेक्ट बनाएं.
  3. Google Maps Platform API और SDK टूल चालू करें. इनकी सूची पिछले सेक्शन में दी गई है.
  4. एपीआई पासकोड जनरेट करें.

Cloud Shell चालू करें

इस कोडलैब में, Cloud Shell का इस्तेमाल किया जाता है. यह Google Cloud में चलने वाला कमांड-लाइन एनवायरमेंट है. इससे Google Cloud पर चलने वाले प्रॉडक्ट और संसाधनों को ऐक्सेस किया जा सकता है, ताकि आप अपने प्रोजेक्ट को पूरी तरह से वेब ब्राउज़र से होस्ट और चला सकें.

Cloud Console से Cloud Shell चालू करने के लिए, Cloud Shell चालू करें 89665d8d348105cd.png पर क्लिक करें. इसे चालू होने और एनवायरमेंट से कनेक्ट होने में कुछ ही समय लगता है.

5f504766b9b3be17.png

इससे आपके ब्राउज़र के निचले हिस्से में एक नई शेल खुलती है. ऐसा हो सकता है कि इससे पहले, आपको एक इंटरस्टीशियल विज्ञापन दिखे.

d3bb67d514893d1f.png

अपने प्रोजेक्ट की पुष्टि करना

Cloud Shell से कनेक्ट होने के बाद, आपको दिखेगा कि आपने पहले ही पुष्टि कर ली है. साथ ही, प्रोजेक्ट को उस प्रोजेक्ट आईडी पर सेट कर दिया गया है जिसे आपने सेटअप के दौरान चुना था.

$ gcloud auth list
Credentialed Accounts:
ACTIVE  ACCOUNT
  *     <myaccount>@<mydomain>.com
$ gcloud config list project
[core]
project = <YOUR_PROJECT_ID>

अगर किसी वजह से प्रोजेक्ट सेट नहीं किया गया है, तो यह कमांड चलाएं:

gcloud config set project <YOUR_PROJECT_ID>

AppEngine Flex API चालू करना

AppEngine Flex API को Cloud Console से मैन्युअल तरीके से चालू करना होगा. ऐसा करने से, एपीआई चालू हो जाएगा. साथ ही, AppEngine फ़्लेक्सिबल एनवायरमेंट सेवा खाता भी बन जाएगा. यह पुष्टि किया गया खाता, उपयोगकर्ता की ओर से Google की सेवाओं (जैसे कि SQL डेटाबेस) के साथ इंटरैक्ट करेगा.

3. नमस्ते

बैकएंड: Go में Hello World

अपने Cloud Shell इंस्टेंस में, आपको सबसे पहले एक Go App Engine फ़्लेक्स ऐप्लिकेशन बनाना होगा. यह ऐप्लिकेशन, बाकी कोडलैब के लिए आधार के तौर पर काम करेगा.

Cloud Shell के टूलबार में, एडिटर खोलें बटन पर क्लिक करें. इससे कोड एडिटर नए टैब में खुल जाएगा. यह वेब आधारित कोड एडिटर है. इसकी मदद से, Cloud Shell इंस्टेंस में मौजूद फ़ाइलों में आसानी से बदलाव किया जा सकता है.

b63f7baad67b6601.png

इसके बाद, एडिटर और टर्मिनल को नए टैब में ले जाने के लिए, नई विंडो में खोलें आइकॉन पर क्लिक करें.

3f6625ff8461c551.png

नए टैब में सबसे नीचे मौजूद टर्मिनल में, नई austin-recycling डायरेक्ट्री बनाएं.

mkdir -p austin-recycling && cd $_

इसके बाद, आपको एक छोटा Go App Engine ऐप्लिकेशन बनाना होगा, ताकि यह पक्का किया जा सके कि सब कुछ काम कर रहा है. Hello World!

austin-recycling डायरेक्ट्री, एडिटर की फ़ोल्डर लिस्ट में बाईं ओर भी दिखनी चाहिए. austin-recycling डायरेक्ट्री में, app.yaml नाम की फ़ाइल बनाएं. app.yaml फ़ाइल में यह कॉन्टेंट डालें:

app.yaml

runtime: go
env: flex

manual_scaling:
  instances: 1
resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

यह कॉन्फ़िगरेशन फ़ाइल, आपके App Engine ऐप्लिकेशन को Go फ़्लेक्स रनटाइम का इस्तेमाल करने के लिए कॉन्फ़िगर करती है. इस फ़ाइल में कॉन्फ़िगरेशन आइटम का मतलब जानने के लिए, Google App Engine Go Standard Environment का दस्तावेज़ देखें.

इसके बाद, app.yaml फ़ाइल के साथ-साथ main.go फ़ाइल बनाएं:

main.go

package main

import (
        "fmt"
        "log"
        "net/http"
        "os"
)

func main() {
        http.HandleFunc("/", handle)
        port := os.Getenv("PORT")
        if port == "" {
                port = "8080"
        }
        log.Printf("Listening on port %s", port)
        if err := http.ListenAndServe(":"+port, nil); err != nil {
                log.Fatal(err)
        }
}

func handle(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/" {
                http.NotFound(w, r)
                return
        }
        fmt.Fprint(w, "Hello world!")
}

यहां कुछ देर रुककर यह समझना ज़रूरी है कि यह कोड क्या करता है. कम से कम, इसके बारे में सामान्य जानकारी होना ज़रूरी है. आपने एक ऐसा पैकेज main तय किया है जो पोर्ट 8080 पर एचटीटीपी सर्वर शुरू करता है. साथ ही, "/" पाथ से मेल खाने वाले एचटीटीपी अनुरोधों के लिए, हैंडलर फ़ंक्शन रजिस्टर करता है.

हैंडलर फ़ंक्शन, जिसे आसानी से handler कहा जाता है, टेक्स्ट स्ट्रिंग "Hello, world!" लिखता है. यह टेक्स्ट आपके ब्राउज़र पर वापस भेज दिया जाएगा, जहां इसे पढ़ा जा सकेगा. आने वाले चरणों में, आपको ऐसे हैंडलर बनाने होंगे जो सामान्य तौर पर हार्ड कोड की गई स्ट्रिंग के बजाय, GeoJSON डेटा के साथ जवाब दें.

यह तरीका अपनाने के बाद, आपको इस तरह का एडिटर दिखेगा:

2084fdd5ef594ece.png

इसे आज़माएं

इस ऐप्लिकेशन को टेस्ट करने के लिए, Cloud Shell इंस्टेंस में App Engine डेवलपमेंट सर्वर को चलाया जा सकता है. Cloud Shell की कमांड लाइन पर वापस जाएं और यह टाइप करें:

go run *.go

आपको लॉग आउटपुट की कुछ लाइनें दिखेंगी. इनसे पता चलेगा कि Cloud Shell इंस्टेंस पर डेवलपमेंट सर्वर चल रहा है. साथ ही, hello world वेब ऐप्लिकेशन, लोकल होस्ट पोर्ट 8080 पर चल रहा है. इस ऐप्लिकेशन पर वेब ब्राउज़र टैब खोला जा सकता है. इसके लिए, वेब की झलक बटन दबाएं. इसके बाद, Cloud Shell टूलबार में मौजूद पोर्ट 8080 पर झलक देखें मेन्यू आइटम चुनें.

4155fc1dc717ac67.png

इस मेन्यू आइटम पर क्लिक करने से, आपके वेब ब्राउज़र में एक नया टैब खुलेगा. इसमें App Engine डेवलपमेंट सर्वर से "Hello, world!" शब्द दिखेंगे.

अगले चरण में, आपको इस ऐप्लिकेशन में ऑस्टिन शहर के रीसाइकलिंग डेटा को जोड़ना होगा. इसके बाद, आपको इसे विज़ुअलाइज़ करना होगा.

4. मौजूदा डेटा पाना

GeoJSON, GIS की दुनिया की सबसे लोकप्रिय भाषा

पिछले चरण में बताया गया था कि आपको अपने Go कोड में हैंडलर बनाने होंगे. ये हैंडलर, वेब ब्राउज़र पर GeoJSON डेटा रेंडर करेंगे. लेकिन GeoJSON क्या है?

जियोग्राफ़िक इन्फ़ॉर्मेशन सिस्टम (GIS) की दुनिया में, हमें कंप्यूटर सिस्टम के बीच भौगोलिक इकाइयों के बारे में जानकारी शेयर करने की ज़रूरत होती है. मैप को लोग आसानी से पढ़ सकते हैं. हालांकि, कंप्यूटर के लिए डेटा को आसानी से समझने वाले फ़ॉर्मैट में उपलब्ध कराना बेहतर होता है.

GeoJSON, भौगोलिक डेटा स्ट्रक्चर को कोड में बदलने का एक फ़ॉर्मैट है. जैसे, ऑस्टिन, टेक्सास में रीसाइकलिंग के लिए सामान छोड़ने की जगहों के निर्देशांक. GeoJSON को इंटरनेट इंजीनियरिंग टास्क फ़ोर्स के स्टैंडर्ड RFC7946 में स्टैंडर्ड के तौर पर शामिल किया गया है. GeoJSON को JSON यानी कि JavaScript Object Notation के हिसाब से तय किया जाता है. इसे ECMA-404 में स्टैंडर्ड के तौर पर शामिल किया गया था. इसे उसी संगठन ने स्टैंडर्ड के तौर पर शामिल किया था जिसने JavaScript को स्टैंडर्ड के तौर पर शामिल किया था. इस संगठन का नाम Ecma International है.

अहम बात यह है कि GeoJSON, भौगोलिक जानकारी को शेयर करने के लिए, वायर फ़ॉर्मैट के तौर पर बड़े पैमाने पर इस्तेमाल किया जाता है. इस कोडलैब में, GeoJSON का इस्तेमाल इन तरीकों से किया जाता है:

  • Go पैकेज का इस्तेमाल करके, ऑस्टिन के डेटा को GIS के खास डेटा स्ट्रक्चर में पार्स करें. इसका इस्तेमाल, अनुरोध किए गए डेटा को फ़िल्टर करने के लिए किया जाएगा.
  • यह कुकी, वेब सर्वर और वेब ब्राउज़र के बीच ट्रांज़िट के लिए अनुरोध किए गए डेटा को क्रम से लगाती है.
  • JavaScript लाइब्रेरी का इस्तेमाल करके, रिस्पॉन्स को मैप पर मार्कर में बदलें.

इससे आपको कोड टाइप करने में काफ़ी समय बचेगा. ऐसा इसलिए, क्योंकि आपको वायर पर मौजूद डेटास्ट्रीम को मेमोरी में मौजूद फ़ॉर्मैट में बदलने के लिए, पार्सर और जनरेटर लिखने की ज़रूरत नहीं होती.

डेटा वापस पाना

ऑस्टिन, टेक्सस का ओपन डेटा पोर्टल, सार्वजनिक संसाधनों के बारे में जियोस्पेशियल जानकारी उपलब्ध कराता है, ताकि लोग इसका इस्तेमाल कर सकें. इस कोडलैब में, आपको रीसाइकलिंग के लिए सामान छोड़ने की जगहों के डेटा सेट को विज़ुअलाइज़ करने का तरीका बताया जाएगा.

आपको मैप पर मार्कर की मदद से डेटा को विज़ुअलाइज़ करना होगा. इसके लिए, Maps JavaScript API के डेटा लेयर का इस्तेमाल किया जाएगा.

शुरुआत में, ऑस्टिन शहर की वेबसाइट से GeoJSON डेटा को अपने ऐप्लिकेशन में डाउनलोड करें.

  1. अपने Cloud Shell इंस्टेंस की कमांड लाइन विंडो में, [CTRL] + [C] टाइप करके सर्वर बंद करें.
  2. austin-recycling डायरेक्ट्री के अंदर data डायरेक्ट्री बनाएं और उस डायरेक्ट्री पर जाएं:
mkdir -p data && cd data

अब रीसाइकलिंग की जगहों की जानकारी पाने के लिए, curl का इस्तेमाल करें:

curl "https://data.austintexas.gov/resource/qzi7-nx8g.geojson" -o recycling-locations.geojson

आखिर में, पैरंट डायरेक्ट्री पर वापस जाएं.

cd ..

5. जगहों को मैप करना

सबसे पहले, app.yaml फ़ाइल को अपडेट करें, ताकि यह ज़्यादा बेहतर ऐप्लिकेशन के बारे में जानकारी दे सके. यह ऐप्लिकेशन, "सिर्फ़ एक Hello World ऐप्लिकेशन नहीं है".

app.yaml

runtime: go
env: flex

handlers:
- url: /
  static_files: static/index.html
  upload: static/index.html
- url: /(.*\.(js|html|css))$
  static_files: static/\1
  upload: static/.*\.(js|html|css)$
- url: /.*
  script: auto

manual_scaling:
  instances: 1
resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

यह app.yaml कॉन्फ़िगरेशन, /, /*.js, /*.css, और /*.html के अनुरोधों को स्टैटिक फ़ाइलों के सेट पर रीडायरेक्ट करता है. इसका मतलब है कि आपके ऐप्लिकेशन के स्टैटिक एचटीएमएल कॉम्पोनेंट को सीधे तौर पर App Engine के फ़ाइल सर्विंग इन्फ़्रास्ट्रक्चर से दिखाया जाएगा, न कि आपके Go ऐप्लिकेशन से. इससे सर्वर पर लोड कम हो जाता है और फ़ाइल दिखाने की स्पीड बढ़ जाती है.

अब Go में अपने ऐप्लिकेशन का बैकएंड बनाएं!

बैक एंड बनाना

आपने शायद ध्यान दिया हो कि आपकी app.yaml फ़ाइल, GeoJSON फ़ाइल को नहीं दिखाती है. ऐसा इसलिए है, क्योंकि GeoJSON को हमारे Go बैकएंड से प्रोसेस किया जाएगा और भेजा जाएगा. इससे हमें बाद के चरणों में कुछ बेहतरीन सुविधाएं बनाने में मदद मिलेगी. अपनी main.go फ़ाइल में यह बदलाव करें:

main.go

package main

import (
        "fmt"
        "log"
        "net/http"
        "os"
        "path/filepath"
)

var GeoJSON = make(map[string][]byte)

// cacheGeoJSON loads files under data into `GeoJSON`.
func cacheGeoJSON() {
        filenames, err := filepath.Glob("data/*")
        if err != nil {
                log.Fatal(err)
        }

        for _, f := range filenames {
                name := filepath.Base(f)
                dat, err := os.ReadFile(f)
                if err != nil {
                        log.Fatal(err)
                }
                GeoJSON[name] = dat
        }
}

func main() {
        // Cache the JSON so it doesn't have to be reloaded every time a request is made.
        cacheGeoJSON()


        // Request for data should be handled by Go.  Everything else should be directed
        // to the folder of static files.
        http.HandleFunc("/data/dropoffs", dropoffsHandler)
        http.Handle("/", http.FileServer(http.Dir("./static/")))

        // Open up a port for the webserver.
        port := os.Getenv("PORT")
        if port == "" {
                port = "8080"
        }
        log.Printf("Listening on port %s", port)

        if err := http.ListenAndServe(":"+port, nil); err != nil {
                log.Fatal(err)
        }
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
        // Writes Hello, World! to the user's web browser via `w`
        fmt.Fprint(w, "Hello, world!")
}

func dropoffsHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-type", "application/json")
        w.Write(GeoJSON["recycling-locations.geojson"])
}

Go बैकएंड पहले से ही हमें एक अहम सुविधा दे रहा है: AppEngine इंस्टेंस, शुरू होते ही उन सभी जगहों की जानकारी को कैश मेमोरी में सेव कर रहा है. इससे समय की बचत होती है, क्योंकि बैकएंड को हर उपयोगकर्ता के हर रीफ़्रेश पर, डिस्क से फ़ाइल को पढ़ने की ज़रूरत नहीं होती!

फ़्रंट एंड बनाना

सबसे पहले, हमें एक फ़ोल्डर बनाना होगा, ताकि हम अपनी सभी स्टैटिक ऐसेट को उसमें सेव कर सकें. अपने प्रोजेक्ट के पैरंट फ़ोल्डर में, static फ़ोल्डर बनाएं.

mkdir -p static && cd static

हम इस फ़ोल्डर में तीन फ़ाइलें बनाएंगे.

  • index.html में, आपके एक पेज वाले स्टोर लोकेटर ऐप्लिकेशन का पूरा एचटीएमएल शामिल होगा.
  • style.css में, आपकी उम्मीद के मुताबिक स्टाइलिंग शामिल होगी
  • app.js, GeoJSON को वापस पाने, Maps API को कॉल करने, और आपके कस्टम मैप पर मार्कर लगाने के लिए ज़िम्मेदार होगा.

इन तीन फ़ाइलों को बनाएं और उन्हें static/ में रखें .

style.css

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
}

#map {
  height: 100%;
  flex-grow: 4;
  flex-basis: auto;
}

index.html

<html>
  <head>
    <title>Austin recycling drop-off locations</title>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <script src="app.js"></script>

    <script
      defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&v=weekly&libraries=places&callback=initialize&solution_channel=GMP_codelabs_fullstackstorelocator_v1_a"
    ></script>
  </head>

  <body>
    <div id="map"></div>
    <!-- Autocomplete div goes here -->
  </body>
</html>

head एलिमेंट के स्क्रिप्ट टैग में मौजूद src यूआरएल पर खास ध्यान दें.

  • प्लेसहोल्डर टेक्स्ट "YOUR_API_KEY" को उस एपीआई कुंजी से बदलें जिसे आपने सेटअप के दौरान जनरेट किया था. अपनी एपीआई कुंजी वापस पाने या नई कुंजी जनरेट करने के लिए, Cloud Console में एपीआई और सेवाएं -> क्रेडेंशियल पेज पर जाएं.
  • ध्यान दें कि यूआरएल में callback=initialize. पैरामीटर शामिल है अब हम उस कॉलबैक फ़ंक्शन वाली JavaScript फ़ाइल बनाएंगे. यहां आपका ऐप्लिकेशन, बैकएंड से जगहों की जानकारी लोड करेगा और उसे Maps API को भेजेगा. इसके बाद, Maps API से मिले नतीजे का इस्तेमाल करके, मैप पर कस्टम जगहों को मार्क करेगा. इन सभी जगहों को आपके वेब पेज पर बेहतर तरीके से रेंडर किया जाएगा.
  • libraries=places पैरामीटर, Places Library को लोड करता है. यह लाइब्रेरी, पते के अपने-आप पूरे होने की सुविधा जैसी सुविधाओं के लिए ज़रूरी है. इन सुविधाओं को बाद में जोड़ा जाएगा.

app.js

let distanceMatrixService;
let map;
let originMarker;
let infowindow;
let circles = [];
let stores = [];
// The location of Austin, TX
const AUSTIN = { lat: 30.262129, lng: -97.7468 };

async function initialize() {
  initMap();

  // TODO: Initialize an infoWindow

  // Fetch and render stores as circles on map
  fetchAndRenderStores(AUSTIN);

  // TODO: Initialize the Autocomplete widget
}

const initMap = () => {
  // TODO: Start Distance Matrix service

  // The map, centered on Austin, TX
  map = new google.maps.Map(document.querySelector("#map"), {
    center: AUSTIN,
    zoom: 14,
    // mapId: 'YOUR_MAP_ID_HERE',
    clickableIcons: false,
    fullscreenControl: false,
    mapTypeControl: false,
    rotateControl: true,
    scaleControl: false,
    streetViewControl: true,
    zoomControl: true,
  });
};

const fetchAndRenderStores = async (center) => {
  // Fetch the stores from the data source
  stores = (await fetchStores(center)).features;

  // Create circular markers based on the stores
  circles = stores.map((store) => storeToCircle(store, map));
};

const fetchStores = async (center) => {
  const url = `/data/dropoffs`;
  const response = await fetch(url);
  return response.json();
};

const storeToCircle = (store, map) => {
  const [lng, lat] = store.geometry.coordinates;
  const circle = new google.maps.Circle({
    radius: 50,
    strokeColor: "#579d42",
    strokeOpacity: 0.8,
    strokeWeight: 5,
    center: { lat, lng },
    map,
  });

  return circle;
};

यह कोड, मैप पर स्टोर की जगहें दिखाता है. अब तक हमने जो भी किया है उसे टेस्ट करने के लिए, कमांड लाइन से पैरंट डायरेक्ट्री पर वापस जाएं:

cd ..

अब डेवलपमेंट मोड में अपने ऐप्लिकेशन को फिर से चलाएं. इसके लिए, इनका इस्तेमाल करें:

go run *.go

इसे पहले की तरह झलक के तौर पर देखें. आपको इस तरह का मैप दिखेगा, जिसमें हरे रंग के छोटे-छोटे गोले होंगे.

58a6680e9c8e7396.png

आपने मैप की जगहों को पहले ही रेंडर कर लिया है. साथ ही, हम कोडलैब के सिर्फ़ आधे हिस्से पर पहुंचे हैं! शानदार. अब कुछ इंटरैक्टिविटी जोड़ते हैं.

6. मांग पर जानकारी दिखाना

मैप मार्कर पर क्लिक इवेंट का जवाब देना

मैप पर कई मार्कर दिखाना एक अच्छा तरीका है. हालांकि, हमें यह भी पक्का करना होगा कि कोई व्यक्ति इनमें से किसी मार्कर पर क्लिक करके, उस जगह के बारे में जानकारी देख सके. जैसे, कारोबार का नाम, पता वगैरह. Google Maps मार्कर पर क्लिक करने पर, आम तौर पर जो छोटी जानकारी वाली विंडो पॉप-अप होती है उसे जानकारी वाली विंडो कहते हैं.

infoWindow ऑब्जेक्ट बनाएं. initialize फ़ंक्शन में यह जोड़ें. साथ ही, टिप्पणी वाली उस लाइन को बदलें जिसमें "// TODO: Initialize an info window" लिखा है.

app.js - initialize

  // Add an info window that pops up when user clicks on an individual
  // location. Content of info window is entirely up to us.
  infowindow = new google.maps.InfoWindow();

fetchAndRenderStores फ़ंक्शन की परिभाषा को इस थोड़े अलग वर्शन से बदलें. इससे आखिरी लाइन में बदलाव होता है. साथ ही, storeToCircle को एक और आर्ग्युमेंट infowindow के साथ कॉल किया जाता है:

app.js - fetchAndRenderStores

const fetchAndRenderStores = async (center) => {
  // Fetch the stores from the data source
  stores = (await fetchStores(center)).features;

  // Create circular markers based on the stores
  circles = stores.map((store) => storeToCircle(store, map, infowindow));
};

storeToCircle की परिभाषा को इस थोड़े लंबे वर्शन से बदलें. इसमें अब तीसरे आर्ग्युमेंट के तौर पर जानकारी वाली विंडो का इस्तेमाल किया जाता है:

app.js - storeToCircle

const storeToCircle = (store, map, infowindow) => {
  const [lng, lat] = store.geometry.coordinates;
  const circle = new google.maps.Circle({
    radius: 50,
    strokeColor: "#579d42",
    strokeOpacity: 0.8,
    strokeWeight: 5,
    center: { lat, lng },
    map,
  });
  circle.addListener("click", () => {
    infowindow.setContent(`${store.properties.business_name}<br />
      ${store.properties.address_address}<br />
      Austin, TX ${store.properties.zip_code}`);
    infowindow.setPosition({ lat, lng });
    infowindow.setOptions({ pixelOffset: new google.maps.Size(0, -30) });
    infowindow.open(map);
  });
  return circle;
};

ऊपर दिए गए नए कोड में, मैप पर मौजूद किसी स्टोर मार्कर पर क्लिक करने पर, चुने गए स्टोर की जानकारी के साथ infoWindow दिखता है.

अगर आपका सर्वर अब भी चल रहा है, तो उसे बंद करके फिर से चालू करें. मैप पेज को रीफ़्रेश करें और मैप मार्कर पर क्लिक करके देखें. कारोबार के नाम और पते के साथ एक छोटी सी जानकारी वाली विंडो पॉप-अप होगी. यह कुछ इस तरह दिखेगी:

1af0ab72ad0eadc5.png

7. उपयोगकर्ता की शुरुआती जगह की जानकारी पाना

स्टोर लोकेटर का इस्तेमाल करने वाले लोग आम तौर पर यह जानना चाहते हैं कि उनके सबसे नज़दीक कौनसी दुकान है या उन्हें उस पते की जानकारी चाहिए जहां से उन्हें अपनी यात्रा शुरू करनी है. जगह के नाम अपने-आप पूरे होने की सुविधा वाला खोज बार जोड़ें, ताकि उपयोगकर्ता आसानी से शुरुआती पता डाल सके. जगह के नाम अपने-आप भरने की सुविधा, टाइप करते समय सुझाव देने की सुविधा देती है. यह सुविधा, Google के अन्य सर्च बार में ऑटोकंप्लीट की सुविधा की तरह ही काम करती है. हालांकि, इसमें Google Maps Platform में मौजूद सभी जगहों के नाम के सुझाव मिलते हैं.

उपयोगकर्ता के इनपुट के लिए फ़ील्ड बनाना

ऑटोकंप्लीट सर्च बार और उससे जुड़े नतीजों के साइड पैनल में स्टाइलिंग जोड़ने के लिए, style.css में जाकर बदलाव करें. सीएसएस स्टाइल अपडेट करते समय, हम आने वाले समय में इस्तेमाल होने वाले साइडबार के लिए भी स्टाइल जोड़ेंगे. यह साइडबार, मैप के साथ स्टोर की जानकारी को सूची के तौर पर दिखाएगा.

इस कोड को फ़ाइल के आखिर में जोड़ें.

style.css

#panel {
  height: 100%;
  flex-basis: 0;
  flex-grow: 0;
  overflow: auto;
  transition: all 0.2s ease-out;
}

#panel.open {
  flex-basis: auto;
}

#panel .place {
  font-family: "open sans", arial, sans-serif;
  font-size: 1.2em;
  font-weight: 500;
  margin-block-end: 0px;
  padding-left: 18px;
  padding-right: 18px;
}

#panel .distanceText {
  color: silver;
  font-family: "open sans", arial, sans-serif;
  font-size: 1em;
  font-weight: 400;
  margin-block-start: 0.25em;
  padding-left: 18px;
  padding-right: 18px;
}

/* Styling for Autocomplete search bar */
#pac-card {
  background-color: #fff;
  border-radius: 2px 0 0 2px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  box-sizing: border-box;
  font-family: Roboto;
  margin: 10px 10px 0 0;
  -moz-box-sizing: border-box;
  outline: none;
}

#pac-container {
  padding-top: 12px;
  padding-bottom: 12px;
  margin-right: 12px;
}

#pac-input {
  background-color: #fff;
  font-family: Roboto;
  font-size: 15px;
  font-weight: 300;
  margin-left: 12px;
  padding: 0 11px 0 13px;
  text-overflow: ellipsis;
  width: 400px;
}

#pac-input:focus {
  border-color: #4d90fe;
}

#pac-title {
  color: #fff;
  background-color: #acbcc9;
  font-size: 18px;
  font-weight: 400;
  padding: 6px 12px;
}

.hidden {
  display: none;
}

ऑटोकंप्लीट की सुविधा वाला खोज बार और स्लाइडआउट पैनल, दोनों ही शुरू में तब तक छिपे रहते हैं, जब तक उनकी ज़रूरत नहीं होती.

index.html में मौजूद "<!-- Autocomplete div goes here -->" वाली टिप्पणी को नीचे दिए गए कोड से बदलकर, Autocomplete विजेट के लिए एक div तैयार करें. इस बदलाव को करते समय, हम स्लाइड आउट पैनल के लिए div भी जोड़ेंगे.

index.html

     <div id="panel" class="closed"></div>
     <div class="hidden">
      <div id="pac-card">
        <div id="pac-title">Find the nearest location</div>
        <div id="pac-container">
          <input
            id="pac-input"
            type="text"
            placeholder="Enter an address"
            class="pac-target-input"
            autocomplete="off"
          />
        </div>
      </div>
    </div>

अब, मैप में Autocomplete विजेट जोड़ने के लिए एक फ़ंक्शन तय करें. इसके लिए, app.js के आखिर में यह कोड जोड़ें.

app.js

const initAutocompleteWidget = () => {
  // Add search bar for auto-complete
  // Build and add the search bar
  const placesAutoCompleteCardElement = document.getElementById("pac-card");
  const placesAutoCompleteInputElement = placesAutoCompleteCardElement.querySelector(
    "input"
  );
  const options = {
    types: ["address"],
    componentRestrictions: { country: "us" },
    map,
  };
  map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
    placesAutoCompleteCardElement
  );
  // Make the search bar into a Places Autocomplete search bar and select
  // which detail fields should be returned about the place that
  // the user selects from the suggestions.
  const autocomplete = new google.maps.places.Autocomplete(
    placesAutoCompleteInputElement,
    options
  );
  autocomplete.setFields(["address_components", "geometry", "name"]);
  map.addListener("bounds_changed", () => {
    autocomplete.setBounds(map.getBounds());
  });

  // TODO: Respond when a user selects an address
};

इस कोड की मदद से, ऑटोकंप्लीट सुविधा के सुझावों में सिर्फ़ पते दिखाए जाते हैं. ऐसा इसलिए, क्योंकि Place Autocomplete सुविधा, कारोबारों के नाम और प्रशासनिक जगहों के नाम भी मैच कर सकती है. साथ ही, इस कोड की मदद से सिर्फ़ अमेरिका के पते दिखाए जाते हैं. इन वैकल्पिक स्पेसिफ़िकेशन को जोड़ने से, उपयोगकर्ता को कम वर्ण डालने होंगे. इससे, अनुमानों को कम किया जा सकेगा, ताकि उपयोगकर्ता को वह पता दिखाया जा सके जिसे वह ढूंढ रहा है.

इसके बाद, यह आपके बनाए गए ऑटोकंप्लीट div को मैप के सबसे ऊपर दाएं कोने में ले जाता है. साथ ही, यह बताता है कि जवाब में हर जगह के बारे में कौनसे फ़ील्ड दिखाए जाने चाहिए.

आखिर में, initialize फ़ंक्शन के आखिर में initAutocompleteWidget फ़ंक्शन को कॉल करें. साथ ही, "// TODO: Initialize the Autocomplete widget" वाली टिप्पणी को बदलें.

app.js - initialize

 // Initialize the Places Autocomplete Widget
 initAutocompleteWidget();

नीचे दिए गए कमांड को चलाकर, अपने सर्वर को रीस्टार्ट करें. इसके बाद, पूर्वावलोकन को रीफ़्रेश करें.

go run *.go

अब आपको अपने मैप के सबसे ऊपर दाएं कोने में, अपने-आप पूरा होने वाला विजेट दिखेगा. इसमें, मैप पर दिख रहे इलाके के हिसाब से, आपके टाइप किए गए पते से मिलते-जुलते अमेरिका के पते दिखेंगे.

58e9bbbcc4bf18d1.png

जब उपयोगकर्ता शुरुआती पता चुनता है, तब मैप को अपडेट करना

अब आपको यह मैनेज करना होगा कि जब उपयोगकर्ता, Autocomplete विजेट से कोई अनुमानित जगह चुनता है, तो उस जगह का इस्तेमाल करके, आपके स्टोर की दूरी का हिसाब लगाया जाए.

app.js में मौजूद initAutocompleteWidget के आखिर में यह कोड जोड़ें. साथ ही, टिप्पणी "// TODO: Respond when a user selects an address" को बदलें.

app.js - initAutocompleteWidget

  // Respond when a user selects an address
  // Set the origin point when the user selects an address
  originMarker = new google.maps.Marker({ map: map });
  originMarker.setVisible(false);
  let originLocation = map.getCenter();
  autocomplete.addListener("place_changed", async () => {
    // circles.forEach((c) => c.setMap(null)); // clear existing stores
    originMarker.setVisible(false);
    originLocation = map.getCenter();
    const place = autocomplete.getPlace();

    if (!place.geometry) {
      // User entered the name of a Place that was not suggested and
      // pressed the Enter key, or the Place Details request failed.
      window.alert("No address available for input: '" + place.name + "'");
      return;
    }
    // Recenter the map to the selected address
    originLocation = place.geometry.location;
    map.setCenter(originLocation);
    map.setZoom(15);
    originMarker.setPosition(originLocation);
    originMarker.setVisible(true);

    // await fetchAndRenderStores(originLocation.toJSON());
    // TODO: Calculate the closest stores
  });

यह कोड एक लिसनर जोड़ता है, ताकि जब उपयोगकर्ता किसी सुझाव पर क्लिक करे, तो मैप चुने गए पते पर फिर से सेंटर हो जाए. साथ ही, दूरी का हिसाब लगाने के लिए, शुरुआती जगह को आधार के तौर पर सेट कर दे. दूरी की कैलकुलेशन को अगले चरण में लागू किया जाता है.

अपने सर्वर को बंद करके फिर से चालू करें. इसके बाद, अपनी झलक को रीफ़्रेश करें. इससे आपको पता चलेगा कि अपने-आप पूरी होने वाली खोज के लिए दिए गए बार में पता डालने के बाद, मैप फिर से सेंटर में आ गया है.

8. Cloud SQL की मदद से स्केल करना

अब तक, हमारे पास स्टोर ढूंढने की सुविधा देने वाला एक बेहतरीन टूल है. यह इस बात का फ़ायदा उठाता है कि ऐप्लिकेशन सिर्फ़ 100 जगहों की जानकारी का इस्तेमाल करेगा. इसलिए, यह उन जगहों की जानकारी को बार-बार फ़ाइल से पढ़ने के बजाय, बैकएंड में मेमोरी में लोड कर देता है. हालांकि, अगर आपके लोकेटर को अलग-अलग स्केल पर काम करना है, तो क्या होगा? अगर आपके पास सैकड़ों ऐसी जगहें हैं जो किसी बड़े भौगोलिक क्षेत्र में फैली हुई हैं या दुनिया भर में हज़ारों जगहें हैं, तो उन सभी जगहों को मेमोरी में सेव करना अब सही नहीं है. साथ ही, ज़ोन को अलग-अलग फ़ाइलों में बांटने से भी समस्याएं आ सकती हैं.

अब डेटाबेस से अपनी जगहों की जानकारी लोड करने का समय आ गया है. इस चरण में, हम आपकी GeoJSON फ़ाइल में मौजूद सभी जगहों की जानकारी को Cloud SQL डेटाबेस में माइग्रेट करेंगे. साथ ही, Go बैकएंड को अपडेट करेंगे, ताकि जब भी कोई अनुरोध आए, तो वह अपने लोकल कैश मेमोरी के बजाय उस डेटाबेस से नतीजे दिखाए.

PostGres डेटाबेस के साथ Cloud SQL इंस्टेंस बनाना

Google Cloud Console के ज़रिए Cloud SQL इंस्टेंस बनाया जा सकता है. हालांकि, कमांड लाइन से इंस्टेंस बनाने के लिए, gcloud यूटिलिटी का इस्तेमाल करना और भी आसान है. क्लाउड शेल में, नीचे दिए गए निर्देश का इस्तेमाल करके Cloud SQL इंस्टेंस बनाएं:

gcloud sql instances create locations \
--database-version=POSTGRES_12 \
--tier=db-custom-1-3840 --region=us-central1
  • आर्ग्युमेंट locations, Cloud SQL के इस इंस्टेंस को दिया गया नाम है.
  • tier फ़्लैग की मदद से, पहले से तय की गई कुछ मशीनों में से किसी एक को आसानी से चुना जा सकता है.
  • वैल्यू db-custom-1-3840 से पता चलता है कि बनाए जा रहे इंस्टेंस में एक वीसीपीयू और करीब 3.75 जीबी मेमोरी होनी चाहिए.

Cloud SQL इंस्टेंस बनाया जाएगा और उसे PostGresSQL डेटाबेस के साथ शुरू किया जाएगा. इसमें डिफ़ॉल्ट उपयोगकर्ता postgres होगा. इस उपयोगकर्ता का पासवर्ड क्या है? यह अच्छा सवाल है! उनके पास कोई रिकॉर्ड नहीं है. आपको लॉग इन करने से पहले, इसे कॉन्फ़िगर करना होगा.

यहां दिए गए कमांड का इस्तेमाल करके पासवर्ड सेट करें:

gcloud sql users set-password postgres \
    --instance=locations --prompt-for-password

इसके बाद, जब आपसे कहा जाए, तब चुना गया पासवर्ड डालें.

PostGIS एक्सटेंशन चालू करना

PostGIS, PostGresSQL के लिए एक एक्सटेंशन है. इससे स्टैंडर्ड टाइप के जियोस्पेशल डेटा को सेव करना आसान हो जाता है. सामान्य तौर पर, हमें अपने डेटाबेस में PostGIS जोड़ने के लिए, इंस्टॉलेशन की पूरी प्रोसेस को पूरा करना होगा. अच्छी बात यह है कि यह PostGresSQL के लिए, Cloud SQL के साथ काम करने वाले एक्सटेंशन में से एक है.

क्लाउड शेल टर्मिनल में, नीचे दिए गए निर्देश का इस्तेमाल करके, उपयोगकर्ता postgres के तौर पर लॉग इन करके डेटाबेस इंस्टेंस से कनेक्ट करें.

gcloud sql connect locations --user=postgres --quiet

अभी बनाया गया पासवर्ड डालें. अब postgres=> कमांड प्रॉम्प्ट पर PostGIS एक्सटेंशन जोड़ें.

CREATE EXTENSION postgis;

अगर एक्सटेंशन बन जाता है, तो आउटपुट में CREATE EXTENSION लिखा होना चाहिए. जैसा कि यहां दिखाया गया है.

कमांड के आउटपुट का उदाहरण

CREATE EXTENSION

आखिर में, postgres=> कमांड प्रॉम्प्ट पर quit कमांड डालकर, डेटाबेस कनेक्शन बंद करें.

\q

डेटाबेस में भौगोलिक डेटा इंपोर्ट करना

अब हमें GeoJSON फ़ाइलों से, उस सभी जगह की जानकारी वाले डेटा को अपने नए डेटाबेस में इंपोर्ट करना है.

अच्छी बात यह है कि यह एक जानी-पहचानी समस्या है. इसे अपने-आप ठीक करने के लिए, इंटरनेट पर कई टूल उपलब्ध हैं. हम ogr2ogr नाम के टूल का इस्तेमाल करेंगे. यह टूल, जियोस्पेशियल डेटा को स्टोर करने के लिए कई सामान्य फ़ॉर्मैट के बीच कन्वर्ज़न करता है. इन विकल्पों में से एक विकल्प, GeoJSON को SQL डंप फ़ाइल में बदलने का है. इसके बाद, SQL डंप फ़ाइल का इस्तेमाल करके डेटाबेस के लिए टेबल और कॉलम बनाए जा सकते हैं. साथ ही, इसे उन सभी डेटा के साथ लोड किया जा सकता है जो आपकी GeoJSON फ़ाइलों में मौजूद था.

एसक्यूएल डंप फ़ाइल बनाना

सबसे पहले, ogr2ogr इंस्टॉल करें.

sudo apt-get install gdal-bin

इसके बाद, SQL डंप फ़ाइल बनाने के लिए ogr2ogr का इस्तेमाल करें. इस फ़ाइल से austinrecycling नाम की टेबल बनेगी.

ogr2ogr --config PG_USE_COPY YES -f PGDump datadump.sql \
data/recycling-locations.geojson -nln austinrecycling

ऊपर दी गई कमांड, austin-recycling फ़ोल्डर से चलाने पर आधारित है. अगर आपको इसे किसी दूसरी डायरेक्ट्री से चलाना है, तो data को उस डायरेक्ट्री के पाथ से बदलें जहां recycling-locations.geojson सेव है.

अपने डेटाबेस में रीसाइकलिंग की जगहों की जानकारी जोड़ना

आखिरी कमांड पूरी होने के बाद, अब आपके पास एक फ़ाइल होनी चाहिए. यह फ़ाइल, datadump.sql, उसी डायरेक्ट्री में होगी जहां आपने कमांड चलाई थी. इसे खोलने पर, आपको 100 से ज़्यादा लाइनों का एसक्यूएल दिखेगा. इससे एक टेबल austinrecycling बनेगी और उसमें जगहों की जानकारी भरी जाएगी.

अब डेटाबेस से कनेक्शन खोलें और इस स्क्रिप्ट को इस कमांड के साथ चलाएं.

gcloud sql connect locations --user=postgres --quiet < datadump.sql

अगर स्क्रिप्ट सही तरीके से चलती है, तो आउटपुट की आखिरी कुछ लाइनें इस तरह दिखेंगी:

कमांड के आउटपुट का उदाहरण

ALTER TABLE
ALTER TABLE
ATLER TABLE
ALTER TABLE
COPY 103
COMMIT
WARNING: there is no transaction in progress
COMMIT

Cloud SQL का इस्तेमाल करने के लिए, Go बैक एंड को अपडेट करना

अब हमारे पास डेटाबेस में यह सारा डेटा है. इसलिए, अब हमें अपने कोड को अपडेट करना होगा.

जगह की जानकारी भेजने के लिए, फ़्रंट एंड को अपडेट करना

आइए, फ़्रंट-एंड में किए गए एक छोटे से अपडेट से शुरुआत करते हैं: अब हम इस ऐप्लिकेशन को ऐसे स्केल के लिए लिख रहे हैं जहां हम नहीं चाहते कि क्वेरी चलाने पर, फ़्रंट-एंड को हर जगह की जानकारी हर बार डिलीवर की जाए. इसलिए, हमें फ़्रंट-एंड से उस जगह के बारे में कुछ बुनियादी जानकारी पास करनी होगी जिसमें उपयोगकर्ता की दिलचस्पी है.

app.js खोलें और fetchStores फ़ंक्शन की परिभाषा को इस वर्शन से बदलें, ताकि यूआरएल में अक्षांश और देशांतर शामिल किया जा सके.

app.js - fetchStores

const fetchStores = async (center) => {
  const url = `/data/dropoffs?centerLat=${center.lat}&centerLng=${center.lng}`;
  const response = await fetch(url);
  return response.json();
};

कोडलैब का यह चरण पूरा करने के बाद, जवाब में सिर्फ़ वे स्टोर दिखेंगे जो center पैरामीटर में दिए गए मैप के कोऑर्डिनेट के सबसे नज़दीक हैं. initialize फ़ंक्शन में शुरुआती फ़ेच के लिए, इस लैब में दिए गए सैंपल कोड में ऑस्टिन, टेक्सस के सेंट्रल कोऑर्डिनेट का इस्तेमाल किया गया है.

fetchStores अब सिर्फ़ स्टोर की कुछ जगहों की जानकारी देगा. इसलिए, जब भी उपयोगकर्ता अपनी शुरुआती जगह की जानकारी बदलेगा, हमें स्टोर की जानकारी फिर से फ़ेच करनी होगी.

initAutocompleteWidget फ़ंक्शन को अपडेट करें, ताकि जब भी कोई नया ऑरिजिन सेट किया जाए, तब जगह की जानकारी रीफ़्रेश हो जाए. इसके लिए, दो बदलाव करने होंगे:

  1. initAutocompleteWidget में, place_changed लिसनर के लिए कॉलबैक ढूंढें. मौजूदा सर्कल को मिटाने वाली लाइन से टिप्पणी हटाने पर, उपयोगकर्ता के Place Autocomplete की खोज से कोई पता चुनने पर, वह लाइन हर बार चलेगी.

app.js - initAutocompleteWidget

  autocomplete.addListener("place_changed", async () => {
    circles.forEach((c) => c.setMap(null)); // clear existing stores
    // ...
  1. चुनी गई मूल जगह बदलने पर, originLocation वैरिएबल अपडेट हो जाता है. "place_changed" कॉलबैक के आखिर में, "// TODO: Calculate the closest stores" लाइन के ऊपर मौजूद लाइन से टिप्पणी हटाने के लिए, "// TODO: Calculate the closest stores" लाइन के ऊपर मौजूद लाइन से टिप्पणी हटाएं. इससे fetchAndRenderStores फ़ंक्शन को नए ऑरिजिन के साथ कॉल किया जा सकेगा.

app.js - initAutocompleteWidget

    await fetchAndRenderStores(originLocation.toJSON());
    // TODO: Calculate the closest stores

फ़्लैट JSON फ़ाइल के बजाय CloudSQL का इस्तेमाल करने के लिए, बैक एंड को अपडेट करें

फ़्लैट-फ़ाइल GeoJSON को पढ़ने और कैश करने की सुविधा हटा दी गई है

सबसे पहले, main.go को बदलकर, फ़्लैट GeoJSON फ़ाइल को लोड और कैश करने वाले कोड को हटाएं. हम dropoffsHandler फ़ंक्शन को भी हटा सकते हैं, क्योंकि हम Cloud SQL की मदद से एक फ़ंक्शन को किसी दूसरी फ़ाइल में लिखेंगे.

आपका नया main.go बहुत छोटा होगा.

main.go

package main

import (

        "log"
        "net/http"
        "os"
)

func main() {

        initConnectionPool()

        // Request for data should be handled by Go.  Everything else should be directed
        // to the folder of static files.
        http.HandleFunc("/data/dropoffs", dropoffsHandler)
        http.Handle("/", http.FileServer(http.Dir("./static/")))

        // Open up a port for the webserver.
        port := os.Getenv("PORT")
        if port == "" {
                port = "8080"
        }
        log.Printf("Listening on port %s", port)
        if err := http.ListenAndServe(":"+port, nil); err != nil {
                log.Fatal(err)
        }
}

जगह की जानकारी के अनुरोधों के लिए नया हैंडलर बनाएं

अब हम austin-recycling डायरेक्ट्री में एक और फ़ाइल, locations.go बनाते हैं. जगह की जानकारी के अनुरोधों के लिए, हैंडलर को फिर से लागू करें.

locations.go

package main

import (
        "database/sql"
        "fmt"
        "log"
        "net/http"
        "os"

        _ "github.com/jackc/pgx/stdlib"
)

// queryBasic demonstrates issuing a query and reading results.
func dropoffsHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-type", "application/json")
        centerLat := r.FormValue("centerLat")
        centerLng := r.FormValue("centerLng")
        geoJSON, err := getGeoJSONFromDatabase(centerLat, centerLng)
        if err != nil {
                str := fmt.Sprintf("Couldn't encode results: %s", err)
                http.Error(w, str, 500)
                return
        }
        fmt.Fprintf(w, geoJSON)
}

हैंडलर ये ज़रूरी काम करता है:

  • यह अनुरोध ऑब्जेक्ट से अक्षांश और देशांतर की जानकारी लेता है. याद रखें कि हमने यूआरएल में यह जानकारी कैसे जोड़ी थी? )
  • यह getGeoJsonFromDatabase कॉल को ट्रिगर करता है. इससे एक GeoJSON स्ट्रिंग मिलती है. (हम इसे बाद में लिखेंगे.)
  • यह ResponseWriter का इस्तेमाल करके, उस GeoJSON स्ट्रिंग को रिस्पॉन्स में प्रिंट करता है.

इसके बाद, हम एक कनेक्शन पूल बनाएंगे. इससे एक साथ कई उपयोगकर्ताओं के डेटाबेस इस्तेमाल करने पर भी, डेटाबेस का इस्तेमाल आसानी से किया जा सकेगा.

कनेक्शन पूल बनाना

कनेक्शन पूल, ऐक्टिव डेटाबेस कनेक्शन का एक कलेक्शन होता है. सर्वर, उपयोगकर्ता के अनुरोधों को पूरा करने के लिए इसका फिर से इस्तेमाल कर सकता है. इससे काफ़ी हद तक ओवरहेड कम हो जाता है, क्योंकि सर्वर को हर सक्रिय उपयोगकर्ता के लिए कनेक्शन बनाने और बंद करने में समय नहीं लगाना पड़ता. आपने पिछले सेक्शन में देखा होगा कि हमने लाइब्रेरी github.com/jackc/pgx/stdlib. इंपोर्ट की है. यह Go में कनेक्शन पूल के साथ काम करने के लिए एक लोकप्रिय लाइब्रेरी है.

locations.go के आखिर में, initConnectionPool फ़ंक्शन बनाएं. इसे main.go से कॉल किया जाता है. यह कनेक्शन पूल को शुरू करता है. इस स्निपेट में कुछ हेल्पर मेथड का इस्तेमाल किया गया है, ताकि इसे आसानी से समझा जा सके. configureConnectionPool की मदद से, पूल की सेटिंग को आसानी से बदला जा सकता है. जैसे, कनेक्शन की संख्या और हर कनेक्शन की लाइफ़टाइम. mustGetEnv, ज़रूरी एनवायरमेंट वैरिएबल पाने के लिए कॉल रैप करता है. इसलिए, अगर इंस्टेंस में ज़रूरी जानकारी (जैसे कि कनेक्ट करने के लिए डेटाबेस का आईपी या नाम) मौजूद नहीं है, तो काम के गड़बड़ी के मैसेज दिखाए जा सकते हैं.

locations.go

// The connection pool
var db *sql.DB

// Each struct instance contains a single row from the query result.
type result struct {
        featureCollection string
}

func initConnectionPool() {
        // If the optional DB_TCP_HOST environment variable is set, it contains
        // the IP address and port number of a TCP connection pool to be created,
        // such as "127.0.0.1:5432". If DB_TCP_HOST is not set, a Unix socket
        // connection pool will be created instead.
        if os.Getenv("DB_TCP_HOST") != "" {
                var (
                        dbUser    = mustGetenv("DB_USER")
                        dbPwd     = mustGetenv("DB_PASS")
                        dbTCPHost = mustGetenv("DB_TCP_HOST")
                        dbPort    = mustGetenv("DB_PORT")
                        dbName    = mustGetenv("DB_NAME")
                )

                var dbURI string
                dbURI = fmt.Sprintf("host=%s user=%s password=%s port=%s database=%s", dbTCPHost, dbUser, dbPwd, dbPort, dbName)

                // dbPool is the pool of database connections.
                dbPool, err := sql.Open("pgx", dbURI)
                if err != nil {
                        dbPool = nil
                        log.Fatalf("sql.Open: %v", err)
                }

                configureConnectionPool(dbPool)

                if err != nil {

                        log.Fatalf("initConnectionPool: unable to connect: %s", err)
                }
                db = dbPool
        }
}

// configureConnectionPool sets database connection pool properties.
// For more information, see https://golang.org/pkg/database/sql
func configureConnectionPool(dbPool *sql.DB) {
        // Set maximum number of connections in idle connection pool.
        dbPool.SetMaxIdleConns(5)
        // Set maximum number of open connections to the database.
        dbPool.SetMaxOpenConns(7)
        // Set Maximum time (in seconds) that a connection can remain open.
        dbPool.SetConnMaxLifetime(1800)
}

// mustGetEnv is a helper function for getting environment variables.
// Displays a warning if the environment variable is not set.
func mustGetenv(k string) string {
        v := os.Getenv(k)
        if v == "" {
                log.Fatalf("Warning: %s environment variable not set.\n", k)
        }
        return v
}

जगहों के बारे में जानकारी पाने के लिए, डेटाबेस से क्वेरी करें और बदले में JSON पाएं.

अब हम एक डेटाबेस क्वेरी लिखेंगे. यह क्वेरी, मैप के निर्देशांक लेती है और सबसे नज़दीकी 25 जगहों की जानकारी देती है. इतना ही नहीं, डेटाबेस की कुछ आधुनिक सुविधाओं की वजह से, यह डेटा को GeoJSON के तौर पर दिखाएगा. इन सभी बदलावों के बाद, फ़्रंट-एंड कोड के हिसाब से कुछ भी नहीं बदला है. इससे पहले कि यह किसी यूआरएल के लिए अनुरोध भेजता और GeoJSON का एक बंडल पाता. अब यह यूआरएल पर एक अनुरोध भेजता है और... इसे GeoJSON का एक बंडल वापस मिलता है.

यहां उस फ़ंक्शन के बारे में बताया गया है जो यह जादू करता है. हैंडलर और कनेक्शन पूलिंग कोड के बाद, locations.go के सबसे नीचे यह फ़ंक्शन जोड़ें.

locations.go

func getGeoJSONFromDatabase(centerLat string, centerLng string) (string, error) {

        // Obviously you can one-line this, but for testing purposes let's make it easy to modify on the fly.
        const milesRadius = 10
        const milesToMeters = 1609
        const radiusInMeters = milesRadius * milesToMeters

        const tableName = "austinrecycling"

        var queryStr = fmt.Sprintf(
                `SELECT jsonb_build_object(
                        'type',
                        'FeatureCollection',
                        'features',
                        jsonb_agg(feature)
                )
        FROM (
                        SELECT jsonb_build_object(
                                        'type',
                                        'Feature',
                                        'id',
                                        ogc_fid,
                                        'geometry',
                                        ST_AsGeoJSON(wkb_geometry)::jsonb,
                                        'properties',
                                        to_jsonb(row) - 'ogc_fid' - 'wkb_geometry'
                                ) AS feature
                        FROM (
                                        SELECT *,
                                                ST_Distance(
                                                        ST_GEOGFromWKB(wkb_geometry),
                                                        -- Los Angeles (LAX)
                                                        ST_GEOGFromWKB(st_makepoint(%v, %v))
                                                ) as distance
                                        from %v
                                        order by distance
                                        limit 25
                                ) row
                        where distance < %v
                ) features
                `, centerLng, centerLat, tableName, radiusInMeters)

        log.Println(queryStr)

        rows, err := db.Query(queryStr)

        defer rows.Close()

        rows.Next()
        queryResult := result{}
        err = rows.Scan(&queryResult.featureCollection)
        return queryResult.featureCollection, err
}

यह फ़ंक्शन ज़्यादातर डेटाबेस को अनुरोध भेजने के लिए, सेटअप, टियरडाउन, और गड़बड़ी को ठीक करने का काम करता है. आइए, असल एसक्यूएल पर एक नज़र डालते हैं. यह डेटाबेस लेयर पर कई दिलचस्प काम करता है, इसलिए आपको कोड में इनमें से किसी को भी लागू करने के बारे में चिंता करने की ज़रूरत नहीं है.

स्ट्रिंग को पार्स करने और सभी स्ट्रिंग लिटरल को उनकी सही जगहों पर डालने के बाद, फ़ायर की गई रॉ क्वेरी इस तरह दिखती है:

parsed.sql

SELECT jsonb_build_object(
        'type',
        'FeatureCollection',
        'features',
        jsonb_agg(feature)
    )
FROM (
        SELECT jsonb_build_object(
                'type',
                'Feature',
                'id',
                ogc_fid,
                'geometry',
                ST_AsGeoJSON(wkb_geometry)::jsonb,
                'properties',
                to_jsonb(row) - 'ogc_fid' - 'wkb_geometry'
            ) AS feature
        FROM (
                SELECT *,
                    ST_Distance(
                        ST_GEOGFromWKB(wkb_geometry),
                        -- Los Angeles (LAX)
                        ST_GEOGFromWKB(st_makepoint(-97.7624043, 30.523725))
                    ) as distance
                from austinrecycling
                order by distance
                limit 25
            ) row
        where distance < 16090
    ) features

इस क्वेरी को एक प्राइमरी क्वेरी और कुछ JSON रैपिंग फ़ंक्शन के तौर पर देखा जा सकता है.

SELECT * ... LIMIT 25 से, हर जगह के लिए सभी फ़ील्ड चुने जाते हैं. इसके बाद, यह ST_DISTANCE फ़ंक्शन का इस्तेमाल करता है. यह फ़ंक्शन, PostGIS के geography measurement functions के सुइट का हिस्सा है. इसका इस्तेमाल, डेटाबेस में मौजूद हर जगह और उपयोगकर्ता की ओर से फ़्रंट-एंड में दी गई जगह के अक्षांश/देशांतर के बीच की दूरी का पता लगाने के लिए किया जाता है. ध्यान रखें कि ये जियोस्पेशल दूरी हैं. ये ड्राइविंग दूरी बताने वाली Distance Matrix से अलग होती हैं. इसके बाद, यह दूरी के हिसाब से जगहों को क्रम में लगाता है और उपयोगकर्ता की बताई गई जगह के हिसाब से, सबसे नज़दीकी 25 जगहों की जानकारी दिखाता है.

**SELECT json_build_object(‘type', ‘F**eature') पिछली क्वेरी को रैप करता है. साथ ही, नतीजों को लेता है और उनका इस्तेमाल करके GeoJSON फ़ीचर ऑब्जेक्ट बनाता है. अचानक से, इस क्वेरी में ज़्यादा से ज़्यादा दायरा भी लागू हो गया. "16090" 10 मील में मीटर की संख्या है. यह Go बैकएंड की तय की गई सीमा है. अगर आपको यह जानना है कि इस WHERE क्लॉज़ को इनर क्वेरी में क्यों नहीं जोड़ा गया (जहां हर जगह की दूरी तय की जाती है), तो इसकी वजह यह है कि SQL, पर्दे के पीछे इस तरह से काम करता है कि WHERE क्लॉज़ की जांच करते समय, उस फ़ील्ड की गिनती नहीं की गई होगी. दरअसल, अगर इस WHERE क्लॉज़ को इनर क्वेरी में ले जाने की कोशिश की जाती है, तो गड़बड़ी का मैसेज दिखेगा.

**SELECT json_build_object(‘type', ‘FeatureColl**ection') इस क्वेरी में, JSON जनरेट करने वाली क्वेरी से मिली सभी पंक्तियों को GeoJSON FeatureCollection ऑब्जेक्ट में रैप किया जाता है.

अपने प्रोजेक्ट में PGX लाइब्रेरी जोड़ना

हमें आपके प्रोजेक्ट में एक डिपेंडेंसी जोड़नी होगी: PostGres Driver & Toolkit. इससे कनेक्शन पूलिंग की सुविधा मिलती है. Go मॉड्यूल का इस्तेमाल करके, इसे आसानी से किया जा सकता है. क्लाउड शेल में इस कमांड का इस्तेमाल करके, मॉड्यूल को शुरू करें:

go mod init my_locator

इसके बाद, डिपेंडेंसी के लिए कोड को स्कैन करने, मॉड फ़ाइल में डिपेंडेंसी की सूची जोड़ने, और उन्हें डाउनलोड करने के लिए यह कमांड चलाएं.

go mod tidy

आखिर में, इस कमांड को चलाएं, ताकि डिपेंडेंसी को सीधे आपकी प्रोजेक्ट डायरेक्ट्री में पुल किया जा सके. इससे AppEngine Flex के लिए कंटेनर को आसानी से बनाया जा सकता है.

go mod vendor

ठीक है, अब आप इसे आज़मा सकते हैं!

इसे आज़माएं

ठीक है, हमने अभी-अभी बहुत कुछ किया है. आइए, देखते हैं कि यह कैसे काम करता है!

आपकी डेवलपमेंट मशीन (हां, क्लाउड शेल भी) को डेटाबेस से कनेक्ट करने के लिए, हमें डेटाबेस कनेक्शन को मैनेज करने के लिए Cloud SQL प्रॉक्सी का इस्तेमाल करना होगा. Cloud SQL Proxy सेट अप करने के लिए:

  1. Cloud SQL Admin API चालू करने के लिए, यहां जाएं
  2. अगर लोकल डेवलपमेंट मशीन का इस्तेमाल किया जा रहा है, तो Cloud SQL प्रॉक्सी टूल इंस्टॉल करें. अगर क्लाउड शेल का इस्तेमाल किया जा रहा है, तो इस चरण को छोड़ दें. यह पहले से इंस्टॉल है! ध्यान दें कि निर्देशों में सेवा खाते का ज़िक्र होगा. आपके लिए पहले से ही एक खाता बना दिया गया है. हम अगले सेक्शन में, उस खाते में ज़रूरी अनुमतियां जोड़ने के बारे में बताएंगे.
  3. प्रॉक्सी शुरू करने के लिए, नया टैब (क्लाउड शेल या अपने टर्मिनल में) बनाएं.

bcca42933bfbd497.png

  1. https://console.cloud.google.com/sql/instances/locations/overview पर जाएं और नीचे की ओर स्क्रोल करके, कनेक्शन का नाम फ़ील्ड ढूंढें. अगले निर्देश में इस्तेमाल करने के लिए, उस नाम को कॉपी करें.
  2. उस टैब में, इस कमांड के साथ Cloud SQL प्रॉक्सी चलाएं. साथ ही, CONNECTION_NAME को पिछले चरण में दिखाए गए कनेक्शन के नाम से बदलें.
cloud_sql_proxy -instances=CONNECTION_NAME=tcp:5432

अपनी क्लाउड शेल के पहले टैब पर वापस जाएं और उन एनवायरमेंट वैरिएबल को तय करें जिनकी ज़रूरत Go को डेटाबेस बैकएंड से कम्यूनिकेट करने के लिए होगी. इसके बाद, सर्वर को उसी तरह चलाएं जैसे आपने पहले चलाया था:

अगर आप प्रोजेक्ट की रूट डायरेक्ट्री में नहीं हैं, तो वहां जाएं.

cd YOUR_PROJECT_ROOT

नीचे दिए गए पांच एनवायरमेंट वैरिएबल बनाएं. YOUR_PASSWORD_HERE को ऊपर बनाए गए पासवर्ड से बदलें.

export DB_USER=postgres
export DB_PASS=YOUR_PASSWORD_HERE
export DB_TCP_HOST=127.0.0.1 # Proxy
export DB_PORT=5432 #Default for PostGres
export DB_NAME=postgres

अपना लोकल इंस्टेंस चलाएं.

go run *.go

झलक दिखाने वाली विंडो खोलें. यह विंडो ऐसे काम करेगी जैसे कुछ भी नहीं बदला है: इसमें शुरुआती पता डाला जा सकता है, मैप को ज़ूम किया जा सकता है, और रीसाइकलिंग की जगहों पर क्लिक किया जा सकता है. हालाँकि, अब इसे डेटाबेस की मदद से तैयार किया गया है और इसे बड़े पैमाने पर इस्तेमाल किया जा सकता है!

9. सबसे नज़दीकी स्टोर की सूची दिखाओ

Directions API, Google Maps ऐप्लिकेशन में दिशा-निर्देशों का अनुरोध करने की सुविधा की तरह काम करता है. इसमें, एक शुरुआती जगह और एक मंज़िल की जानकारी डालकर, दोनों के बीच का रास्ता पाया जा सकता है. Distance Matrix API, इस कॉन्सेप्ट को आगे बढ़ाता है. यह यात्रा में लगने वाले समय और दूरी के आधार पर, यात्रा शुरू करने की कई संभावित जगहों और मंज़िल की कई संभावित जगहों के बीच सबसे सही कॉम्बिनेशन की पहचान करता है. इस मामले में, उपयोगकर्ता को चुने गए पते के हिसाब से सबसे नज़दीकी स्टोर ढूंढने में मदद करने के लिए, आपको एक ओरिजन और स्टोर की जगहों की एक सीरीज़ को डेस्टिनेशन के तौर पर सेट करना होगा.

हर स्टोर के लिए, मूल जगह से दूरी की जानकारी जोड़ें

initMap फ़ंक्शन की परिभाषा की शुरुआत में, "// TODO: Start Distance Matrix service" टिप्पणी को इस कोड से बदलें:

app.js - initMap

distanceMatrixService = new google.maps.DistanceMatrixService();

app.js के आखिर में calculateDistances नाम का एक नया फ़ंक्शन जोड़ें.

app.js

async function calculateDistances(origin, stores) {
  // Retrieve the distances of each store from the origin
  // The returned list will be in the same order as the destinations list
  const response = await getDistanceMatrix({
    origins: [origin],
    destinations: stores.map((store) => {
      const [lng, lat] = store.geometry.coordinates;
      return { lat, lng };
    }),
    travelMode: google.maps.TravelMode.DRIVING,
    unitSystem: google.maps.UnitSystem.METRIC,
  });
  response.rows[0].elements.forEach((element, index) => {
    stores[index].properties.distanceText = element.distance.text;
    stores[index].properties.distanceValue = element.distance.value;
  });
}

const getDistanceMatrix = (request) => {
  return new Promise((resolve, reject) => {
    const callback = (response, status) => {
      if (status === google.maps.DistanceMatrixStatus.OK) {
        resolve(response);
      } else {
        reject(response);
      }
    };
    distanceMatrixService.getDistanceMatrix(request, callback);
  });
};

यह फ़ंक्शन, Distance Matrix API को कॉल करता है. इसमें, इसे पास किए गए ऑरिजिन को एक ऑरिजिन के तौर पर इस्तेमाल किया जाता है. साथ ही, स्टोर की जगहों की जानकारी को डेस्टिनेशन के अरे के तौर पर इस्तेमाल किया जाता है. इसके बाद, यह ऑब्जेक्ट का एक ऐसा ऐरे बनाता है जिसमें स्टोर का आईडी, इंसानों के पढ़ने लायक स्ट्रिंग में दूरी, मीटर में दूरी को न्यूमेरिक वैल्यू के तौर पर सेव किया जाता है. साथ ही, ऐरे को क्रम से लगाता है.

initAutocompleteWidget फ़ंक्शन को अपडेट करें, ताकि जगह के नाम अपने-आप भरने वाले खोज बार से कोई नया ऑरिजिन चुनने पर, स्टोर की दूरी का हिसाब लगाया जा सके. initAutocompleteWidget फ़ंक्शन में सबसे नीचे, "// TODO: Calculate the closest stores" वाली टिप्पणी को इस कोड से बदलें:

app.js - initAutocompleteWidget

    // Use the selected address as the origin to calculate distances
    // to each of the store locations
    await calculateDistances(originLocation, stores);
    renderStoresPanel();

दूरी के हिसाब से क्रम में लगाए गए स्टोर की सूची वाला व्यू दिखाना

उपयोगकर्ता को स्टोर की सूची, सबसे नज़दीकी स्टोर से लेकर सबसे दूर के स्टोर तक के क्रम में दिखनी चाहिए. हर स्टोर के लिए साइड-पैनल की सूची भरें. इसके लिए, calculateDistances फ़ंक्शन से बदली गई सूची का इस्तेमाल करें, ताकि स्टोर के दिखने का क्रम तय किया जा सके.

app.js के आखिर में दो नए फ़ंक्शन जोड़ें. इनके नाम renderStoresPanel() और storeToPanelRow() हैं.

app.js

function renderStoresPanel() {
  const panel = document.getElementById("panel");

  if (stores.length == 0) {
    panel.classList.remove("open");
    return;
  }

  // Clear the previous panel rows
  while (panel.lastChild) {
    panel.removeChild(panel.lastChild);
  }
  stores
    .sort((a, b) => a.properties.distanceValue - b.properties.distanceValue)
    .forEach((store) => {
      panel.appendChild(storeToPanelRow(store));
    });
  // Open the panel
  panel.classList.add("open");
  return;
}

const storeToPanelRow = (store) => {
  // Add store details with text formatting
  const rowElement = document.createElement("div");
  const nameElement = document.createElement("p");
  nameElement.classList.add("place");
  nameElement.textContent = store.properties.business_name;
  rowElement.appendChild(nameElement);
  const distanceTextElement = document.createElement("p");
  distanceTextElement.classList.add("distanceText");
  distanceTextElement.textContent = store.properties.distanceText;
  rowElement.appendChild(distanceTextElement);
  return rowElement;
};

अपने सर्वर को रीस्टार्ट करें और इस कमांड को चलाकर अपनी झलक को रीफ़्रेश करें.

go run *.go

आखिर में, अपने-आप पूरा होने वाले खोज बार में ऑस्टिन, TX का पता डालें और किसी एक सुझाव पर क्लिक करें.

मैप पर वह पता बीच में दिखना चाहिए. साथ ही, एक साइडबार दिखना चाहिए, जिसमें स्टोर की जगहों की सूची, चुने गए पते से दूरी के हिसाब से क्रम में दी गई हो. यहां एक उदाहरण दिया गया है:

96e35794dd0e88c9.png

10. मैप का स्टाइल तय करना

अपने मैप को विज़ुअल तौर पर अलग दिखाने का एक असरदार तरीका है, उसमें स्टाइलिंग जोड़ना. क्लाउड पर मैप की स्टाइलिंग की सुविधा का इस्तेमाल करके, Cloud Console से मैप को अपनी पसंद के मुताबिक बनाया जा सकता है. इसके लिए, क्लाउड पर मैप की स्टाइलिंग (बीटा वर्शन) का इस्तेमाल करें. अगर आपको बीटा वर्शन में शामिल नहीं की गई किसी सुविधा का इस्तेमाल करके, अपने मैप को स्टाइल करना है, तो मैप स्टाइलिंग के दस्तावेज़ का इस्तेमाल करें. इससे आपको प्रोग्राम के हिसाब से मैप को स्टाइल करने के लिए, JSON जनरेट करने में मदद मिलेगी. यहां दिए गए निर्देशों में, क्लाउड पर मैप की स्टाइलिंग (बीटा वर्शन) के बारे में बताया गया है.

मैप आईडी बनाना

सबसे पहले, Cloud Console खोलें. इसके बाद, खोज बॉक्स में "Map Management" टाइप करें. "मैप मैनेजमेंट (Google Maps)" वाले नतीजे पर क्लिक करें. 64036dd0ed200200.png

आपको सबसे ऊपर (सर्च बॉक्स के ठीक नीचे) एक बटन दिखेगा. इस पर नया मैप आईडी बनाएं लिखा होगा. उस पर क्लिक करें और अपनी पसंद का नाम डालें. मैप टाइप के लिए, JavaScript को ज़रूर चुनें. इसके बाद, जब ज़्यादा विकल्प दिखें, तो सूची से वेक्टर चुनें. आखिर में, इमेज कुछ ऐसी दिखनी चाहिए.

70f55a759b4c4212.png

"आगे बढ़ें" पर क्लिक करें. इसके बाद, आपको नया मैप आईडी मिलेगा. अगर आपको यह कोड अभी कॉपी करना है, तो इसे कॉपी करें. हालांकि, इसे बाद में भी आसानी से देखा जा सकता है.

इसके बाद, हम उस मैप पर लागू करने के लिए एक स्टाइल बनाएंगे.

मैप स्टाइल बनाना

अगर आप अब भी Cloud Console के Maps सेक्शन में हैं, तो बाईं ओर मौजूद नेविगेशन मेन्यू में सबसे नीचे, "मैप स्टाइल पर क्लिक करें. इसके अलावा, मैप आईडी बनाने की तरह ही, खोज बॉक्स में "मैप की स्टाइल" टाइप करके सही पेज ढूंढा जा सकता है. इसके बाद, नतीजों में से " मैप की स्टाइल (Google Maps)" चुनें. यह विकल्प, नीचे दी गई इमेज में दिखाया गया है.

9284cd200f1a9223.png

इसके बाद, सबसे ऊपर मौजूद "+ नई मैप स्टाइल बनाएं" बटन पर क्लिक करें

  1. अगर आपको इस लैब में दिखाए गए मैप की स्टाइलिंग से मैच करना है, तो "JSON इंपोर्ट करें" टैब पर क्लिक करें और नीचे दिए गए JSON ब्लोब को चिपकाएं. अगर आपको अपनी पसंद के मुताबिक मैप स्टाइल बनानी है, तो वह मैप स्टाइल चुनें जिससे आपको शुरुआत करनी है. उसके बाद, आगे बढ़ें पर क्लिक करें.
  2. आपने जो मैप आईडी बनाया है उसे इस स्टाइल से जोड़ने के लिए, उस मैप आईडी को चुनें. इसके बाद, आगे बढ़ें पर फिर से क्लिक करें.
  3. इस चरण में, आपको अपने मैप की स्टाइल को और ज़्यादा कस्टमाइज़ करने का विकल्प मिलता है. अगर आपको इस सुविधा के बारे में ज़्यादा जानना है, तो स्टाइल एडिटर में पसंद के मुताबिक बनाएं पर क्लिक करें. इसके बाद, अपनी पसंद के मुताबिक मैप की स्टाइल बनाने के लिए, रंगों और विकल्पों का इस्तेमाल करें. अगर आपको ऐसा नहीं करना है, तो अभी नहीं पर क्लिक करें.
  4. अगले चरण में, अपने स्टाइल का नाम और ब्यौरा डालें. इसके बाद, सेव करें और पब्लिश करें पर क्लिक करें.

यहां पहले चरण में इंपोर्ट करने के लिए, एक JSON blob दिया गया है. हालांकि, इसे इंपोर्ट करना ज़रूरी नहीं है.

[
  {
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#d6d2c4"
      }
    ]
  },
  {
    "elementType": "labels.icon",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#616161"
      }
    ]
  },
  {
    "elementType": "labels.text.stroke",
    "stylers": [
      {
        "color": "#f5f5f5"
      }
    ]
  },
  {
    "featureType": "administrative.land_parcel",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#bdbdbd"
      }
    ]
  },
  {
    "featureType": "landscape.man_made",
    "elementType": "geometry.fill",
    "stylers": [
      {
        "color": "#c0baa5"
      },
      {
        "visibility": "on"
      }
    ]
  },
  {
    "featureType": "landscape.man_made",
    "elementType": "geometry.stroke",
    "stylers": [
      {
        "color": "#9cadb7"
      },
      {
        "visibility": "on"
      }
    ]
  },
  {
    "featureType": "poi",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#757575"
      }
    ]
  },
  {
    "featureType": "poi.park",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#9e9e9e"
      }
    ]
  },
  {
    "featureType": "road",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#ffffff"
      }
    ]
  },
  {
    "featureType": "road.arterial",
    "elementType": "geometry",
    "stylers": [
      {
        "weight": 1
      }
    ]
  },
  {
    "featureType": "road.arterial",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#757575"
      }
    ]
  },
  {
    "featureType": "road.highway",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#bf5700"
      }
    ]
  },
  {
    "featureType": "road.highway",
    "elementType": "geometry.stroke",
    "stylers": [
      {
        "visibility": "off"
      }
    ]
  },
  {
    "featureType": "road.highway",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#616161"
      }
    ]
  },
  {
    "featureType": "road.local",
    "elementType": "geometry",
    "stylers": [
      {
        "weight": 0.5
      }
    ]
  },
  {
    "featureType": "road.local",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#9e9e9e"
      }
    ]
  },
  {
    "featureType": "transit.line",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#e5e5e5"
      }
    ]
  },
  {
    "featureType": "transit.station",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#eeeeee"
      }
    ]
  },
  {
    "featureType": "water",
    "elementType": "geometry",
    "stylers": [
      {
        "color": "#333f48"
      }
    ]
  },
  {
    "featureType": "water",
    "elementType": "labels.text.fill",
    "stylers": [
      {
        "color": "#9e9e9e"
      }
    ]
  }
]

अपने कोड में मैप आईडी जोड़ना

अब आपने इस मैप स्टाइल को बना लिया है. तो अब इस मैप स्टाइल को अपने मैप में कैसे इस्तेमाल करें? आपको दो छोटे-छोटे बदलाव करने होंगे:

  1. index.html में स्क्रिप्ट टैग में मैप आईडी को यूआरएल पैरामीटर के तौर पर जोड़ें
  2. Add मैप आईडी को कंस्ट्रक्टर आर्ग्युमेंट के तौर पर इस्तेमाल करें. ऐसा तब करें, जब आपको initMap() तरीके से मैप बनाना हो.

एचटीएमएल फ़ाइल में, Maps JavaScript API को लोड करने वाले स्क्रिप्ट टैग को यहां दिए गए लोडर यूआरएल से बदलें. साथ ही, "YOUR_API_KEY" और "YOUR_MAP_ID" के प्लेसहोल्डर बदलें:

index.html

...
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&v=weekly&libraries=places&callback=initialize&map_ids=YOUR_MAP_ID&solution_channel=GMP_codelabs_fullstackstorelocator_v1_a">
  </script>
...

app.js के initMap तरीके में, जहां कॉन्सटेंट map को तय किया गया है वहां mapId प्रॉपर्टी के लिए लाइन से अनकमेंट करें. इसके बाद, "YOUR_MAP_ID_HERE" को उस मैप आईडी से बदलें जिसे आपने अभी बनाया है:

app.js - initMap

...

// The map, centered on Austin, TX
 const map = new google.maps.Map(document.querySelector('#map'), {
   center: austin,
   zoom: 14,
   mapId: 'YOUR_MAP_ID_HERE',
// ...
});
...

अपना सर्वर रीस्टार्ट करें.

go run *.go

झलक को रीफ़्रेश करने पर, मैप आपकी पसंद के मुताबिक स्टाइल में दिखना चाहिए. यहां ऊपर दी गई JSON स्टाइलिंग का इस्तेमाल करके एक उदाहरण दिया गया है.

2ece59c64c06e9da.png

11. प्रोडक्शन में डिप्लॉय करना

अगर आपको अपने ऐप्लिकेशन को AppEngine Flex से चलाना है, तो यह बहुत आसान है. इसके लिए, आपको सिर्फ़ अपनी डेवलपमेंट मशीन / Cloud Shell पर लोकल वेबसर्वर का इस्तेमाल नहीं करना होगा. डेटाबेस ऐक्सेस को प्रोडक्शन एनवायरमेंट में काम करने के लिए, हमें बस कुछ चीज़ें जोड़नी होंगी. इस बारे में पूरी जानकारी, App Engine Flex से Cloud SQL को कनेक्ट करना लेख में दी गई है.

App.yaml फ़ाइल में एनवायरमेंट वैरिएबल जोड़ना

सबसे पहले, उन सभी एनवायरमेंट वैरिएबल को अपने ऐप्लिकेशन की app.yaml फ़ाइल के सबसे नीचे जोड़ें जिनका इस्तेमाल आपने स्थानीय तौर पर जांच करने के लिए किया था.

  1. इंस्टेंस कनेक्शन का नाम देखने के लिए, https://console.cloud.google.com/sql/instances/locations/overview पर जाएं.
  2. नीचे दिए गए कोड को app.yaml के आखिर में चिपकाएं.
  3. YOUR_DB_PASSWORD_HERE को उस पासवर्ड से बदलें जिसे आपने postgres उपयोगकर्ता नाम के लिए पहले बनाया था.
  4. YOUR_CONNECTION_NAME_HERE की जगह, पहले चरण में मिली वैल्यू डालें.

app.yaml

# ...
# Set environment variables
env_variables:
    DB_USER: postgres
    DB_PASS: YOUR_DB_PASSWORD_HERE
    DB_NAME: postgres
    DB_TCP_HOST: 172.17.0.1
    DB_PORT: 5432

#Enable TCP Port
# You can look up your instance connection name by going to the page for
# your instance in the Cloud Console here : https://console.cloud.google.com/sql/instances/
beta_settings:
  cloud_sql_instances: YOUR_CONNECTION_NAME_HERE=tcp:5432

ध्यान दें कि DB_TCP_HOST की वैल्यू 172.17.0.1 होनी चाहिए, क्योंकि यह ऐप्लिकेशन AppEngine Flex** के ज़रिए कनेक्ट होता है.** ऐसा इसलिए, क्योंकि यह Cloud SQL से प्रॉक्सी के ज़रिए कम्यूनिकेट करेगा. यह ठीक उसी तरह से कम्यूनिकेट करेगा जैसे आपने किया था.

AppEngine Flex सेवा खाते में SQL क्लाइंट की अनुमतियां जोड़ना

Cloud Console में IAM-Admin पेज पर जाएं और उस सेवा खाते को ढूंढें जिसका नाम service-PROJECT_NUMBER@gae-api-prod.google.com.iam.gserviceaccount.com फ़ॉर्मैट से मेल खाता हो. यह सेवा खाता, App Engine Flex डेटाबेस से कनेक्ट करने के लिए इस्तेमाल करेगा. लाइन के आखिर में मौजूद, बदलाव करें बटन पर क्लिक करें. इसके बाद, "Cloud SQL Client" भूमिका जोड़ें.

b04ccc0b4022b905.png

अपने प्रोजेक्ट कोड को Go पाथ पर कॉपी करें

AppEngine को आपका कोड चलाने के लिए, Go पाथ में काम की फ़ाइलें ढूंढनी होती हैं. पक्का करें कि आप अपने प्रोजेक्ट की रूट डायरेक्ट्री में हों.

cd YOUR_PROJECT_ROOT

डायरेक्ट्री को गो पाथ पर कॉपी करें.

mkdir -p ~/gopath/src/austin-recycling
cp -r ./ ~/gopath/src/austin-recycling

उस डायरेक्ट्री में जाएं.

cd ~/gopath/src/austin-recycling

अपना ऐप्लिकेशन डिप्लॉय करना

अपने ऐप्लिकेशन को डिप्लॉय करने के लिए, gcloud CLI का इस्तेमाल करें. इसे डिप्लॉय होने में कुछ समय लगेगा.

gcloud app deploy

browse कमांड का इस्तेमाल करके, एक ऐसा लिंक पाएं जिस पर क्लिक करके, अपने पूरी तरह से डिप्लॉय किए गए, एंटरप्राइज़-ग्रेड, और देखने में शानदार स्टोर लोकेटर को ऐक्शन में देखा जा सकता है.

gcloud app browse

अगर आपने क्लाउड शेल के बाहर gcloud चलाया था, तो gcloud app browse चलाने पर ब्राउज़र का एक नया टैब खुलेगा.

12. (सुझाया गया) साफ़ करें

इस कोडलैब को पूरा करने पर, BigQuery प्रोसेसिंग और Maps Platform API कॉल के लिए, मुफ़्त टियर की सीमाएं बनी रहेंगी. हालांकि, अगर आपने इसे सिर्फ़ सीखने के मकसद से पूरा किया है और आपको आने वाले समय में कोई शुल्क नहीं देना है, तो इस प्रोजेक्ट से जुड़े संसाधनों को मिटाने का सबसे आसान तरीका है कि आप प्रोजेक्ट को ही मिटा दें.

प्रोजेक्ट मिटाना

GCP Console में, Cloud Resource Manager पेज पर जाएं:

प्रोजेक्ट की सूची में, वह प्रोजेक्ट चुनें जिस पर हम काम कर रहे थे. इसके बाद, मिटाएं पर क्लिक करें. आपको प्रोजेक्ट आईडी डालने के लिए कहा जाएगा. इसे डालें और बंद करें पर क्लिक करें.

इसके अलावा, Cloud Shell से सीधे तौर पर पूरे प्रोजेक्ट को मिटाया जा सकता है. इसके लिए, gcloud का इस्तेमाल करके यह कमांड चलाएं और प्लेसहोल्डर GOOGLE_CLOUD_PROJECT की जगह अपना प्रोजेक्ट आईडी डालें:

gcloud projects delete GOOGLE_CLOUD_PROJECT

13. बधाई हो

बधाई हो! आपने कोडलैब (कोड बनाना सिखाने वाला ट्यूटोरियल) पूरा कर लिया है!

या आपने आखिरी पेज पर जाकर देखा हो. बधाई हो! आपने आखिरी पेज पर जाकर, सरसरी तौर पर जानकारी देखी है!

इस कोडलैब के दौरान, आपने इन टेक्नोलॉजी का इस्तेमाल किया:

इसके बारे में और पढ़ें

इन सभी टेक्नोलॉजी के बारे में अब भी बहुत कुछ सीखना बाकी है. यहां कुछ ऐसे मददगार लिंक दिए गए हैं जिनके बारे में हम इस कोडलैब में नहीं बता पाए. हालांकि, ये लिंक आपके लिए तब काम के हो सकते हैं, जब आपको अपनी ज़रूरतों के हिसाब से स्टोर ढूंढने की सुविधा बनाने में मदद चाहिए.