Tạo một bộ định vị cửa hàng đơn giản bằng Nền tảng Google Maps (JavaScript)

1. Trước khi bắt đầu

Một trong những tính năng phổ biến nhất của trang web là hiển thị bản đồ của Google để làm nổi bật một hoặc nhiều vị trí cho doanh nghiệp, cơ sở hoặc một số pháp nhân khác có mặt tại đó. Cách các bản đồ này được triển khai có thể thay đổi rất nhiều tùy thuộc vào các yêu cầu, chẳng hạn như số lượng vị trí và tần suất mà các bản đồ này thay đổi.

Trong lớp học lập trình này, bạn xem xét trường hợp sử dụng đơn giản nhất – một số vị trí hiếm khi thay đổi, chẳng hạn như bộ định vị cửa hàng cho doanh nghiệp có một chuỗi cửa hàng. Trong trường hợp này, bạn có thể sử dụng phương pháp công nghệ tương đối thấp mà không cần lập trình phía máy chủ. Nhưng điều đó không có nghĩa là bạn không thể sáng tạo và bạn làm như vậy bằng cách tận dụng định dạng dữ liệu GeoJSON để lưu trữ và hiển thị thông tin tùy ý về từng cửa hàng trên bản đồ của bạn, cũng như tùy chỉnh các điểm đánh dấu và kiểu tổng thể của bản đồ.

Cuối cùng, như một lợi ích khác, bạn sử dụng Cloud Shell để phát triển và lưu trữ công cụ định vị cửa hàng của mình. Mặc dù việc sử dụng công cụ này không bắt buộc, nhưng việc này cho phép bạn phát triển bộ định vị cửa hàng từ bất kỳ thiết bị nào chạy trình duyệt web và hiển thị công khai trên mạng.

489628918395c3d0.png

Điều kiện tiên quyết

  • Kiến thức cơ bản về HTML và JavaScript

Bạn sẽ thực hiện

  • Hiển thị bản đồ có một tập hợp các vị trí cửa hàng và thông tin được lưu trữ ở định dạng GeoJSON.
  • Tùy chỉnh các điểm đánh dấu và bản đồ.
  • Hiện thêm thông tin về cửa hàng khi người dùng nhấp vào điểm đánh dấu của cửa hàng.
  • Thêm thanh tìm kiếm Tự động hoàn thành địa điểm vào trang web.
  • Xác định vị trí cửa hàng gần nhất với điểm xuất phát do người dùng cung cấp.

2. Bắt đầu thiết lập

Ở bước 3 của phần sau, hãy bật 3 API sau đây cho lớp học lập trình này:

  • API JavaScript cho Maps
  • Places API
  • Distance Matrix API

Bắt đầu sử dụng Nền tảng Google Maps

Nếu bạn chưa từng sử dụng Nền tảng Google Maps, hãy làm theo Hướng dẫn bắt đầu sử dụng Nền tảng Google Maps hoặc xem Danh sách phát Bắt đầu với Nền tảng Google Maps để hoàn thành các bước sau:

  1. Tạo một tài khoản thanh toán.
  2. Tạo một dự án.
  3. Bật API và SDK của nền tảng Google Maps (được liệt kê trong phần trước).
  4. Tạo khoá API.

Kích hoạt Cloud Shell

Trong lớp học lập trình này, bạn sử dụng Cloud Shell, một môi trường dòng lệnh chạy trong Google Cloud, cung cấp quyền truy cập vào các sản phẩm và tài nguyên chạy trên Google Cloud để bạn có thể lưu trữ và chạy hoàn toàn dự án của mình trên trình duyệt web.

Để kích hoạt Cloud Shell từ Cloud Console, hãy nhấp vào Kích hoạt Cloud Shell 89665d8d348105cd.png (chỉ mất vài phút để cấp phép và kết nối với môi trường).

5f504766b9b3be17.png

Thao tác này sẽ mở một màn hình shell mới ở phần dưới của trình duyệt sau khi có thể hiển thị quảng cáo xen kẽ giới thiệu.

d3bb67d514893d1f.png

Sau khi kết nối với Cloud Shell, bạn sẽ thấy rằng bạn đã được xác thực và dự án này đã được đặt thành mã dự án mà bạn đã chọn trong quá trình thiết lập.

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

Nếu vì lý do nào đó mà dự án chưa được đặt, hãy chạy lệnh sau:

$ gcloud config set project <YOUR_PROJECT_ID>

3. "Hello, World!" cùng với một bản đồ

Bắt đầu phát triển bằng một bản đồ

Trong Cloud Shell, bạn bắt đầu bằng cách tạo một trang HTML sẽ phân phát làm cơ sở cho phần còn lại của lớp học lập trình.

  1. Trong thanh công cụ của Cloud Shell, hãy nhấp vào Chạy trình chỉnh sửa 996514928389de40.png để mở một trình soạn thảo mã trong thẻ mới.

Trình chỉnh sửa mã dựa trên web này cho phép bạn dễ dàng chỉnh sửa các tệp trong Cloud Shell.

Ảnh chụp màn hình lúc 10:22,48 sáng ngày 19/4/2017

  1. Tạo thư mục store-locator mới cho ứng dụng của bạn trong trình soạn thảo mã bằng cách nhấp vào Tệp > Thư mục mới.

NewThư.png

  1. Đặt tên cho thư mục mới là store-locator.

Tiếp theo, bạn tạo một trang web có bản đồ.

  1. Tạo một tệp trong thư mục store-locator có tên index.html.

3c257603da5ab524.png

  1. Đặt nội dung sau đây vào tệp index.html:

index.html

<html>

<head>
    <title>Store Locator</title>
    <style>
        #map {
            height: 100%;
        }
        
        html,
        body {
            height: 100%;
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <!-- The div to hold the map -->
    <div id="map"></div>

    <script src="app.js"></script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap&solution_channel=GMP_codelabs_simplestorelocator_v1_a">
    </script>
</body>

</html>

Đây là trang HTML hiển thị bản đồ. Tệp này chứa một số CSS để đảm bảo bản đồ sẽ chiếm toàn bộ trang, một thẻ <div> để giữ bản đồ và một cặp thẻ <script>. Thẻ tập lệnh đầu tiên tải một tệp JavaScript có tên là app.js, tệp này chứa tất cả mã JavaScript. Thẻ tập lệnh thứ hai tải khóa API, bao gồm việc sử dụng Thư viện địa điểm để sử dụng chức năng tự động hoàn thành mà bạn sẽ thêm sau này và chỉ định tên hàm JavaScript chạy sau khi API JavaScript của Maps được tải, cụ thể là initMap.

  1. Thay thế văn bản YOUR_API_KEY trong đoạn mã bằng khóa API mà bạn đã tạo trước đó trong lớp học lập trình này.
  2. Cuối cùng, hãy tạo một tệp khác có tên app.js bằng mã sau:

app.js

function initMap() {
   // Create the map.
    const map = new google.maps.Map(document.getElementById('map'), {
        zoom: 7,
        center: { lat: 52.632469, lng: -1.689423 },
    });

}

Đó là mã bắt buộc tối thiểu để tạo bản đồ. Bạn chuyển tham chiếu đến thẻ <div> để giữ bản đồ, đồng thời chỉ định phần giữa và mức thu phóng.

Để kiểm tra ứng dụng này, bạn có thể chạy máy chủ HTTP Python đơn giản trong Cloud Shell.

  1. Truy cập Cloud Shell và nhập nội dung sau:
$ cd store-locator
$ python3 -m http.server 8080

Bạn thấy một số dòng kết quả nhật ký cho biết rằng bạn đang thực sự chạy máy chủ HTTP đơn giản trong Cloud Shell bằng ứng dụng web đang nghe trên cổng localhost 8080.

  1. Mở một thẻ trình duyệt web trên ứng dụng này bằng cách nhấp vào Xem trước trên web 95e419ae763a1d48.pngtrong thanh công cụ Cloud Console rồi chọn Xem trước trên cổng 8080.

47b06e5169eb5add.png

bdab1f021a3b91d5.png

Nhấp vào mục trình đơn này sẽ mở ra một thẻ mới trong trình duyệt web có nội dung HTML được phân phát từ máy chủ HTTP đơn giản. Nếu mọi thứ diễn ra tốt đẹp, bạn sẽ thấy một bản đồ tập trung vào London, Anh.

Để dừng máy chủ HTTP đơn giản, nhấn Control+C trong Cloud Shell.

4. Điền sẵn bản đồ bằng GeoJSON

Bây giờ, hãy xem dữ liệu của các cửa hàng. XMLJSON là một định dạng dữ liệu đại diện cho các đối tượng địa lý đơn giản, chẳng hạn như điểm, đường hoặc đa giác trên bản đồ. Các tính năng cũng có thể chứa dữ liệu bất kỳ. Điều này giúp GeoJSON trở thành một ứng cử viên xuất sắc khi đại diện cho cửa hàng, về cơ bản là những điểm trên bản đồ có một chút dữ liệu bổ sung, chẳng hạn như tên, giờ mở cửa và số điện thoại của cửa hàng. Điều quan trọng nhất là GeoJSON có hỗ trợ hạng nhất trong Google Maps, có nghĩa là bạn có thể gửi tài liệu GeoJSON đến một bản đồ Google và nó sẽ hiển thị nó trên bản đồ một cách thích hợp.

  1. Tạo một tệp mới có tên là stores.json và dán mã sau:

store.json

{
    "type": "FeatureCollection",
    "features": [{
            "geometry": {
                "type": "Point",
                "coordinates": [-0.1428115,
                    51.5125168
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Modern twists on classic pastries. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Mayfair",
                "phone": "+44 20 1234 5678",
                "storeid": "01"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-2.579623,
                    51.452251
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Come and try our award-winning cakes and pastries. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Bristol",
                "phone": "+44 117 121 2121",
                "storeid": "02"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [
                    1.273459,
                    52.638072
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Whatever the occasion, whether it's a birthday or a wedding, Josie's Patisserie has the perfect treat for you. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Norwich",
                "phone": "+44 1603 123456",
                "storeid": "03"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-1.9912838,
                    50.8000418
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "A gourmet patisserie that will delight your senses. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Wimborne",
                "phone": "+44 1202 343434",
                "storeid": "04"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-2.985933,
                    53.408899
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Spoil yourself or someone special with our classic pastries. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Liverpool",
                "phone": "+44 151 444 4444",
                "storeid": "05"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-1.689423,
                    52.632469
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Come and feast your eyes and tastebuds on our delicious pastries and cakes. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Tamworth",
                "phone": "+44 5555 55555",
                "storeid": "06"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-3.155305,
                    51.479756
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "patisserie",
                "hours": "10am - 6pm",
                "description": "Josie's Patisserie is family-owned, and our delectable pastries, cakes, and great coffee are renowed. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Patisserie Cardiff",
                "phone": "+44 29 6666 6666",
                "storeid": "07"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-0.725019,
                    52.668891
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Oakham's favorite spot for fresh coffee and delicious cakes. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Oakham",
                "phone": "+44 7777 777777",
                "storeid": "08"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-2.477653,
                    53.735405
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Enjoy freshly brewed coffe, and home baked cakes in our homely cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Blackburn",
                "phone": "+44 8888 88888",
                "storeid": "09"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-0.211363,
                    51.108966
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "A delicious array of pastries with many flavours, and fresh coffee in an snug cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Crawley",
                "phone": "+44 1010 101010",
                "storeid": "10"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-0.123559,
                    50.832679
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Grab a freshly brewed coffee, a decadent cake and relax in our idyllic cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Brighton",
                "phone": "+44 1313 131313",
                "storeid": "11"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [-3.319575,
                    52.517827
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Come in and unwind at this idyllic cafe with fresh coffee and home made cakes. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Newtown",
                "phone": "+44 1414 141414",
                "storeid": "12"
            }
        },
        {
            "geometry": {
                "type": "Point",
                "coordinates": [
                    1.158167,
                    52.071634
                ]
            },
            "type": "Feature",
            "properties": {
                "category": "cafe",
                "hours": "8am - 9:30pm",
                "description": "Fresh coffee and delicious cakes in an snug cafe. We're part of a larger chain of patisseries and cafes.",
                "name": "Josie's Cafe Ipswich",
                "phone": "+44 1717 17171",
                "storeid": "13"
            }
        }
    ]
}

Đó là rất nhiều dữ liệu, nhưng khi bạn kiểm tra kỹ, bạn sẽ thấy cấu trúc đó chỉ đơn giản là cấu trúc lặp lại cho mỗi cửa hàng. Mỗi cửa hàng được trình bày dưới dạng GeoJSON Point cùng với tọa độ và dữ liệu bổ sung chứa trong khóa properties. Điều thú vị là GeoJSON cho phép bao gồm các khóa được đặt tên tùy ý trong khóa properties. Trong lớp học lập trình này, các khóa đó là category, hours, description, namephone.

  1. Bây giờ, hãy chỉnh sửa app.js để tải JSONJSON trong stores.js lên bản đồ của bạn.

app.js

function initMap() {
  // Create the map.
  const map = new google.maps.Map(document.getElementById('map'), {
    zoom: 7,
    center: {lat: 52.632469, lng: -1.689423},
  });

  // Load the stores GeoJSON onto the map.
  map.data.loadGeoJson('stores.json', {idPropertyName: 'storeid'});

  const apiKey = 'YOUR_API_KEY';
  const infoWindow = new google.maps.InfoWindow();

  // Show the information for a store when its marker is clicked.
  map.data.addListener('click', (event) => {
    const category = event.feature.getProperty('category');
    const name = event.feature.getProperty('name');
    const description = event.feature.getProperty('description');
    const hours = event.feature.getProperty('hours');
    const phone = event.feature.getProperty('phone');
    const position = event.feature.getGeometry().get();
    const content = `
      <h2>${name}</h2><p>${description}</p>
      <p><b>Open:</b> ${hours}<br/><b>Phone:</b> ${phone}</p>
    `;

    infoWindow.setContent(content);
    infoWindow.setPosition(position);
    infoWindow.setOptions({pixelOffset: new google.maps.Size(0, -30)});
    infoWindow.open(map);
  });
}

Trong ví dụ về mã, bạn đã tải GeoJSON vào bản đồ bằng cách gọi loadGeoJson và chuyển tên của tệp JSON. Bạn cũng đã xác định một hàm để chạy mỗi lần nhấp vào điểm đánh dấu. Sau đó, hàm này có thể truy cập vào dữ liệu bổ sung của cửa hàng có điểm đánh dấu đã nhấp vào và sử dụng thông tin trong cửa sổ thông tin hiển thị. Để kiểm tra ứng dụng này, bạn có thể chạy máy chủ HTTP Python đơn giản bằng cách sử dụng lệnh giống như trước đây.

  1. Quay lại Cloud Shell và nhập nội dung sau:
$ python3 -m http.server 8080
  1. Nhấp vào Xem trước trên web 95e419ae763a1d48.png > Xem trước trên cổng 8080 lần nữa và bạn sẽ thấy bản đồ có đầy đủ các điểm đánh dấu mà bạn có thể nhấp vào để xem chi tiết về từng cửa hàng, như trong ví dụ sau. Tiến trình!

c4507f7d3ea18439.png

5. Tùy chỉnh bản đồ

Bạn sắp hoàn thành rồi. Bạn có một bản đồ với tất cả các điểm đánh dấu cửa hàng và thông tin bổ sung đang hiển thị khi được nhấp vào. Tuy nhiên, có vẻ như mọi Google khác đều lập bản đồ tại đó. Thật tuyệt! Tăng giá trị bằng cách sử dụng kiểu bản đồ, điểm đánh dấu, biểu trưng và hình ảnh Chế độ xem phố tùy chỉnh.

Đây là phiên bản mới của app.js có thêm kiểu tùy chỉnh:

app.js

const mapStyle = [{
  'featureType': 'administrative',
  'elementType': 'all',
  'stylers': [{
    'visibility': 'on',
  },
  {
    'lightness': 33,
  },
  ],
},
{
  'featureType': 'landscape',
  'elementType': 'all',
  'stylers': [{
    'color': '#f2e5d4',
  }],
},
{
  'featureType': 'poi.park',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#c5dac6',
  }],
},
{
  'featureType': 'poi.park',
  'elementType': 'labels',
  'stylers': [{
    'visibility': 'on',
  },
  {
    'lightness': 20,
  },
  ],
},
{
  'featureType': 'road',
  'elementType': 'all',
  'stylers': [{
    'lightness': 20,
  }],
},
{
  'featureType': 'road.highway',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#c5c6c6',
  }],
},
{
  'featureType': 'road.arterial',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#e4d7c6',
  }],
},
{
  'featureType': 'road.local',
  'elementType': 'geometry',
  'stylers': [{
    'color': '#fbfaf7',
  }],
},
{
  'featureType': 'water',
  'elementType': 'all',
  'stylers': [{
    'visibility': 'on',
  },
  {
    'color': '#acbcc9',
  },
  ],
},
];

function initMap() {
  // Create the map.
  const map = new google.maps.Map(document.getElementById('map'), {
    zoom: 7,
    center: {lat: 52.632469, lng: -1.689423},
    styles: mapStyle,
  });

  // Load the stores GeoJSON onto the map.
  map.data.loadGeoJson('stores.json', {idPropertyName: 'storeid'});

  // Define the custom marker icons, using the store's "category".
  map.data.setStyle((feature) => {
    return {
      icon: {
        url: `img/icon_${feature.getProperty('category')}.png`,
        scaledSize: new google.maps.Size(64, 64),
      },
    };
  });

  const apiKey = 'YOUR_API_KEY';
  const infoWindow = new google.maps.InfoWindow();

  // Show the information for a store when its marker is clicked.
  map.data.addListener('click', (event) => {
    const category = event.feature.getProperty('category');
    const name = event.feature.getProperty('name');
    const description = event.feature.getProperty('description');
    const hours = event.feature.getProperty('hours');
    const phone = event.feature.getProperty('phone');
    const position = event.feature.getGeometry().get();
    const content = `
      <img style="float:left; width:200px; margin-top:30px" src="img/logo_${category}.png">
      <div style="margin-left:220px; margin-bottom:20px;">
        <h2>${name}</h2><p>${description}</p>
        <p><b>Open:</b> ${hours}<br/><b>Phone:</b> ${phone}</p>
        <p><img src="https://maps.googleapis.com/maps/api/streetview?size=350x120&location=${position.lat()},${position.lng()}&key=${apiKey}&solution_channel=GMP_codelabs_simplestorelocator_v1_a"></p>
      </div>
      `;

    infoWindow.setContent(content);
    infoWindow.setPosition(position);
    infoWindow.setOptions({pixelOffset: new google.maps.Size(0, -30)});
    infoWindow.open(map);
  });

}

Đây là thông tin bạn đã thêm:

  • Biến mapStyle chứa tất cả thông tin để định kiểu cho bản đồ. (Ngoài ra, bạn có thể tạo kiểu quảng cáo của riêng mình nếu muốn.)
  • Bằng cách sử dụng phương thức map.data.setStyle, bạn đã áp dụng các điểm đánh dấu tùy chỉnh – một điểm đánh dấu khác nhau cho mỗi category từ GeoJSON.
  • Bạn đã sửa đổi biến content để bao gồm biểu trưng (một lần nữa bằng cách sử dụng category từ GeoJSON) và hình ảnh Chế độ xem phố cho vị trí của cửa hàng.

Trước khi triển khai thẻ này, bạn cần hoàn thành một vài bước sau:

  1. Đặt giá trị chính xác cho biến apiKey bằng cách thay thế chuỗi 'YOUR_API_KEY' trong app.js bằng khóa API của riêng bạn (trước đó là khóa mà bạn đã dán trong index.html), để nguyên dấu ngoặc kép.
  2. Chạy các lệnh sau trong Cloud Shell để tải đồ họa điểm đánh dấu và biểu trưng xuống. Hãy đảm bảo bạn đang ở trong thư mục store-locator. Dùng Control+C để dừng máy chủ HTTP đơn giản nếu máy chủ đang chạy.
$ mkdir -p img; cd img
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/icon_cafe.png
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/icon_patisserie.png
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/logo_cafe.png
$ wget https://github.com/googlecodelabs/google-maps-simple-store-locator/raw/master/src/img/logo_patisserie.png
  1. Xem trước bộ định vị cửa hàng đã hoàn thành bằng cách chạy lệnh sau:
$ python3 -m http.server 8080

Khi tải lại bản xem trước, bạn sẽ thấy nội dung như bản đồ này với kiểu tùy chỉnh, hình ảnh điểm đánh dấu tùy chỉnh, định dạng cửa sổ thông tin được cải thiện và hình ảnh Chế độ xem phố cho mỗi vị trí:

3d8d13da126021dd.png

6. Lấy thông tin do người dùng nhập

Người dùng công cụ định vị cửa hàng thường muốn biết cửa hàng nào gần họ nhất hoặc một địa chỉ mà họ dự định bắt đầu hành trình của mình. Thêm thanh tìm kiếm Tự động hoàn thành địa điểm để cho phép người dùng dễ dàng nhập địa chỉ bắt đầu. Tính năng Tự động hoàn thành địa điểm cung cấp chức năng đánh dấu tương tự như cách hoạt động của tính năng Tự động hoàn thành trong các thanh tìm kiếm khác của Google, nhưng cụm từ gợi ý đều là tất cả Địa điểm trong Nền tảng Google Maps.

  1. Quay lại chỉnh sửa index.html để thêm kiểu cho thanh tìm kiếm Tự động hoàn thành và bảng kết quả bên được liên kết. Đừng quên thay thế khóa API nếu bạn đã dán mã cũ.

index.html

<html>

<head>
  <title>Store Locator</title>
  <style>
    #map {
      height: 100%;
    }
    
    html,
    body {
      height: 100%;
      margin: 0;
      padding: 0;
    }

    /* 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;
    }
    
    #title {
      color: #fff;
      background-color: #acbcc9;
      font-size: 18px;
      font-weight: 400;
      padding: 6px 12px;
    }
    
    .hidden {
      display: none;
    }

    /* Styling for an info pane that slides out from the left. 
     * Hidden by default. */
    #panel {
      height: 100%;
      width: null;
      background-color: white;
      position: fixed;
      z-index: 1;
      overflow-x: hidden;
      transition: all .2s ease-out;
    }
    
    .open {
      width: 250px;
    }
    
    .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;
    }
    
    .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;
    }
  </style>
</head>

<body>
  <!-- The div to hold the map -->
  <div id="map"></div>

  <script src="app.js"></script>
  <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap&solution_channel=GMP_codelabs_simplestorelocator_v1_a">
  </script>
</body>

</html>

Cả thanh tìm kiếm của tính năng Tự động hoàn thành và bảng trượt sẽ đều bị ẩn cho đến khi cần thiết.

  1. Bây giờ, hãy thêm tiện ích Tự động hoàn thành vào bản đồ ở cuối hàm initMap trong app.js, ngay trước dấu ngoặc nhọn đóng.

app.js

  // Build and add the search bar
  const card = document.createElement('div');
  const titleBar = document.createElement('div');
  const title = document.createElement('div');
  const container = document.createElement('div');
  const input = document.createElement('input');
  const options = {
    types: ['address'],
    componentRestrictions: {country: 'gb'},
  };

  card.setAttribute('id', 'pac-card');
  title.setAttribute('id', 'title');
  title.textContent = 'Find the nearest store';
  titleBar.appendChild(title);
  container.setAttribute('id', 'pac-container');
  input.setAttribute('id', 'pac-input');
  input.setAttribute('type', 'text');
  input.setAttribute('placeholder', 'Enter an address');
  container.appendChild(input);
  card.appendChild(titleBar);
  card.appendChild(container);
  map.controls[google.maps.ControlPosition.TOP_RIGHT].push(card);

  // 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(input, options);

  autocomplete.setFields(
      ['address_components', 'geometry', 'name']);

Mã này hạn chế các nội dung đề xuất Tự động hoàn thành chỉ trả về địa chỉ (vì tính năng Tự động hoàn thành địa điểm có thể khớp với tên cơ sở và vị trí quản trị) và giới hạn địa chỉ được trả về chỉ những địa chỉ ở Vương quốc Anh. Việc thêm các thông số tùy chọn này sẽ giảm số lượng ký tự mà người dùng cần nhập để thu hẹp cụm từ gợi ý để hiển thị địa chỉ mà họ đang tìm kiếm. Sau đó, hệ thống này sẽ di chuyển div Tự động hoàn thành mà bạn đã tạo vào góc trên cùng bên phải của bản đồ và chỉ định trường nào sẽ được trả về về mỗi Địa điểm trong nội dung phản hồi.

  1. Khởi động lại máy chủ và làm mới bản xem trước của bạn bằng cách chạy lệnh sau:
$ python3 -m http.server 8080

Bạn sẽ thấy tiện ích Tự động hoàn thành ở góc trên cùng bên phải của bản đồ ngay bây giờ, tiện ích này cho bạn biết địa chỉ ở Vương quốc Anh khớp với nội dung bạn nhập.

5163f34a03910187.png

Giờ đây, bạn cần xử lý khi người dùng chọn một cụm từ gợi ý từ tiện ích Tự động hoàn thành và sử dụng vị trí đó làm cơ sở để tính khoảng cách đến các cửa hàng của bạn.

  1. Thêm mã sau vào cuối initMap trong app.js sau mã bạn vừa dán.

app.js

 // Set the origin point when the user selects an address
  const originMarker = new google.maps.Marker({map: map});
  originMarker.setVisible(false);
  let originLocation = map.getCenter();

  autocomplete.addListener('place_changed', async () => {
    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(9);
    console.log(place);

    originMarker.setPosition(originLocation);
    originMarker.setVisible(true);

    // Use the selected address as the origin to calculate distances
    // to each of the store locations
    const rankedStores = await calculateDistances(map.data, originLocation);
    showStoresList(map.data, rankedStores);

    return;
  });

Mã này sẽ thêm một trình xử lý để khi người dùng nhấp vào một trong các đề xuất, bản đồ sẽ được căn cứ vào địa chỉ đã chọn và đặt nguồn gốc làm cơ sở cho các phép tính khoảng cách của bạn. Bạn triển khai các phép tính khoảng cách trong bước tiếp theo.

7. Liệt kê các cửa hàng gần nhất

API Chỉ đường hoạt động giống như trải nghiệm yêu cầu chỉ đường trong ứng dụng Google Maps — nhập một nguồn gốc và một điểm đến duy nhất để nhận tuyến đường giữa hai mục tiêu. API Ma trận khoảng cách nâng cao khái niệm này để xác định những cặp ghép tối ưu giữa nhiều nguồn gốc có thể có và nhiều điểm đến có thể dựa trên thời gian di chuyển và khoảng cách. Trong trường hợp này, để giúp người dùng tìm cửa hàng gần nhất đến địa chỉ đã chọn, bạn cung cấp một nguồn gốc và một mảng vị trí cửa hàng làm điểm đến.

  1. Thêm một hàm mới vào app.js có tên là calculateDistances.

app.js

async function calculateDistances(data, origin) {
  const stores = [];
  const destinations = [];

  // Build parallel arrays for the store IDs and destinations
  data.forEach((store) => {
    const storeNum = store.getProperty('storeid');
    const storeLoc = store.getGeometry().get();

    stores.push(storeNum);
    destinations.push(storeLoc);
  });

  // Retrieve the distances of each store from the origin
  // The returned list will be in the same order as the destinations list
  const service = new google.maps.DistanceMatrixService();
  const getDistanceMatrix =
    (service, parameters) => new Promise((resolve, reject) => {
      service.getDistanceMatrix(parameters, (response, status) => {
        if (status != google.maps.DistanceMatrixStatus.OK) {
          reject(response);
        } else {
          const distances = [];
          const results = response.rows[0].elements;
          for (let j = 0; j < results.length; j++) {
            const element = results[j];
            const distanceText = element.distance.text;
            const distanceVal = element.distance.value;
            const distanceObject = {
              storeid: stores[j],
              distanceText: distanceText,
              distanceVal: distanceVal,
            };
            distances.push(distanceObject);
          }

          resolve(distances);
        }
      });
    });

  const distancesList = await getDistanceMatrix(service, {
    origins: [origin],
    destinations: destinations,
    travelMode: 'DRIVING',
    unitSystem: google.maps.UnitSystem.METRIC,
  });

  distancesList.sort((first, second) => {
    return first.distanceVal - second.distanceVal;
  });

  return distancesList;
}

Hàm này gọi API Ma trận khoảng cách bằng cách sử dụng nguồn gốc được chuyển đến dưới dạng một nguồn gốc và các vị trí cửa hàng dưới dạng một mảng đích. Sau đó, Analytics xây dựng một mảng đối tượng lưu trữ mã cửa hàng, khoảng cách biểu thị trong một chuỗi mà con người có thể đọc được, khoảng cách tính bằng mét dưới dạng một giá trị số và sắp xếp mảng.

Người dùng cần xem danh sách cửa hàng được sắp xếp theo thứ tự từ gần nhất đến xa nhất. Điền thông tin vào bảng điều khiển bên cho mỗi cửa hàng bằng cách sử dụng danh sách được trả về từ hàm calculateDistances để thông báo thứ tự hiển thị của các cửa hàng.

  1. Thêm một hàm mới vào app.js có tên là showStoresList.

app.js

function showStoresList(data, stores) {
  if (stores.length == 0) {
    console.log('empty stores');
    return;
  }

  let panel = document.createElement('div');
  // If the panel already exists, use it. Else, create it and add to the page.
  if (document.getElementById('panel')) {
    panel = document.getElementById('panel');
    // If panel is already open, close it
    if (panel.classList.contains('open')) {
      panel.classList.remove('open');
    }
  } else {
    panel.setAttribute('id', 'panel');
    const body = document.body;
    body.insertBefore(panel, body.childNodes[0]);
  }


  // Clear the previous details
  while (panel.lastChild) {
    panel.removeChild(panel.lastChild);
  }

  stores.forEach((store) => {
    // Add store details with text formatting
    const name = document.createElement('p');
    name.classList.add('place');
    const currentStore = data.getFeatureById(store.storeid);
    name.textContent = currentStore.getProperty('name');
    panel.appendChild(name);
    const distanceText = document.createElement('p');
    distanceText.classList.add('distanceText');
    distanceText.textContent = store.distanceText;
    panel.appendChild(distanceText);
  });

  // Open the panel
  panel.classList.add('open');

  return;
}
  1. Khởi động lại máy chủ và làm mới bản xem trước của bạn bằng cách chạy lệnh sau.
$ python3 -m http.server 8080
  1. Cuối cùng, hãy nhập địa chỉ ở Vương quốc Anh vào thanh tìm kiếm Tự động hoàn thành rồi nhấp vào một trong các đề xuất.

Bản đồ phải căn giữa vào địa chỉ đó và thanh bên sẽ xuất hiện liệt kê các vị trí cửa hàng theo khoảng cách từ địa chỉ đã chọn. Dưới đây là một ví dụ:

489628918395c3d0.png

8. Không bắt buộc: Lưu trữ trang web

Cho đến thời điểm này, bạn chỉ xem bản đồ của mình khi bạn đang chạy máy chủ HTTP Python. Để xem bản đồ của bạn sau phiên Cloud Shell đang hoạt động hoặc để chia sẻ URL cho bản đồ của bạn với người khác, hãy xem cách sử dụng Cloud Storage để lưu trữ trang web của bạn. Cloud Storage là dịch vụ web lưu trữ tệp trực tuyến để lưu trữ và truy cập vào dữ liệu trên cơ sở hạ tầng của Google. Dịch vụ này kết hợp hiệu suất và khả năng mở rộng của Google Cloud với khả năng chia sẻ và bảo mật nâng cao. Dịch vụ này cũng cung cấp một Bậc miễn phí, phù hợp để lưu trữ công cụ định vị cửa hàng đơn giản của bạn.

Với Cloud Storage, các tệp được lưu trữ trong các nhóm, tương tự như các thư mục trên máy tính của bạn. Để lưu trữ trang web, trước tiên, bạn cần tạo một bộ chứa. Bạn cần chọn một tên duy nhất cho bộ chứa của mình, có thể bằng cách sử dụng tên của bạn làm tên của bộ chứa.

  1. Sau khi bạn quyết định tên, hãy chạy lệnh sau trong Cloud Shell:
$ gsutil mb gs://yourname-store-locator

gsutil là công cụ để tương tác với Cloud Storage. Lệnh mb sáng tạo là viết tắt "make bundle." Để biết thêm thông tin về tất cả các lệnh có sẵn, bao gồm cả những lệnh bạn sử dụng, hãy xem công cụ gsutil.

Theo mặc định, các nhóm và tệp được lưu trữ trên Cloud Storage sẽ ở chế độ riêng tư. Tuy nhiên, đối với bộ định vị cửa hàng, bạn muốn công khai tất cả các tệp sao cho mọi người có thể truy cập được qua Internet. Bạn có thể đặt từng tệp ở chế độ công khai sau khi tải tệp đó lên, nhưng việc này sẽ rất nhàm chán. Thay vào đó, bạn chỉ cần đặt cấp truy cập mặc định cho bộ chứa mà bạn đã tạo và tất cả tệp bạn tải lên sẽ kế thừa cấp truy cập đó.

  1. Chạy lệnh sau, thay thế yourname-store-locator bằng tên mà bạn đã chọn cho bộ chứa của mình:
$ gsutil defacl ch -u AllUsers:R gs://yourname-store-locator
  1. Giờ đây, bạn có thể tải tất cả tệp lên thư mục hiện tại (hiện chỉ có tệp index.htmlapp.js) bằng lệnh sau:
$ gsutil -h "Cache-Control:no-cache" cp * gs://yourname-store-locator

Giờ đây, bạn sẽ có một trang web với bản đồ trực tuyến. URL để xem URL đó sẽ là http://storage.googleapis.com/yourname-store-locator/index.html, một lần nữa với phần yourname-store-locator được thay thế bằng tên bộ chứa mà bạn đã chọn trước đó.

Dọn dẹp

Cách dễ dàng nhất để dọn dẹp tất cả các tài nguyên được tạo trong dự án này là tắt Dự án Google Cloud mà bạn đã tạo ở đầu hướng dẫn này:

  • Mở Trang cài đặt trong Cloud Console
  • Nhấp vào Chọn một dự án.
  • Chọn dự án mà bạn đã tạo ở đầu hướng dẫn này rồi nhấp vào Mở
  • Nhập mã dự án rồi nhấp vào Tắt.

9. Xin chúc mừng

Xin chúc mừng! Bạn đã hoàn thành lớp học lập trình này.

Những điều bạn đã học được

Tìm hiểu thêm

Bạn muốn xem những lớp học lập trình nào khác?

Hình ảnh dữ liệu trên bản đồ Tìm hiểu thêm về cách tùy chỉnh kiểu bản đồ của tôi Xây dựng hoạt động tương tác 3D trong bản đồ

Lớp học lập trình mà bạn muốn không được liệt kê ở trên? Yêu cầu phát hành vấn đề mới tại đây.

Nếu bạn muốn tìm hiểu sâu hơn về mã này, hãy xem kho mã nguồn tại https://github.com/googlecodelabs/google-maps-simple-store-locator.