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, trong đó làm nổi bật một hoặc nhiều vị trí của một doanh nghiệp, cơ sở hoặc một số thực thể khác có sự hiện diện thực tế. Cách triển khai các bản đồ này có thể khác nhau rất nhiều tuỳ 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 thay đổi.
Trong lớp học lập trình này, bạn sẽ xem xét trường hợp sử dụng đơn giản nhất: một số ít vị trí hiếm khi thay đổi, chẳng hạn như một công cụ định vị cửa hàng cho một doanh nghiệp có chuỗi cửa hàng. Trong trường hợp này, bạn có thể sử dụng một phương pháp tương đối đơn giản mà không cần lập trình phía máy chủ. Tuy nhiên, điều đó không có nghĩa là bạn không thể sáng tạo. Bạn có thể sáng tạo 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 tuỳ ý về từng cửa hàng trên bản đồ, cũng như tuỳ chỉnh các điểm đánh dấu và phong cách tổng thể của bản đồ.
Cuối cùng, bạn có thể sử dụng Cloud Shell để phát triển và lưu trữ công cụ định vị cửa hàng. Mặc dù không bắt buộc phải sử dụng công cụ này, nhưng việc sử dụng sẽ giúp bạn phát triển công cụ định vị cửa hàng trên mọi thiết bị chạy trình duyệt web và cung cấp công cụ này cho công chúng trên mạng.
Đ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 nhóm vị trí cửa hàng và thông tin được lưu trữ ở định dạng GeoJSON.
- Tuỳ chỉnh điểm đánh dấu và bản đồ.
- Hiển thị 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 một thanh tìm kiếm tính năng 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 bắt đầu do người dùng cung cấp.
2. Bắt đầu thiết lập
Trong Bước 3 của phần sau, hãy bật 3 API sau 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 sử dụng Nền tảng Google Maps để hoàn tất các bước sau:
- Tạo tài khoản thanh toán.
- Tạo dự án.
- Bật các API và SDK của Nền tảng Google Maps (được liệt kê trong phần trước).
- Tạo khoá API.
Kích hoạt Cloud Shell
Trong lớp học lập trình này, bạn sẽ sử dụng Cloud Shell, một môi trường dòng lệnh chạy trong Google Cloud, cho phép truy cập vào các sản phẩm và tài nguyên chạy trên Google Cloud, nhờ đó bạn có thể lưu trữ và chạy dự án hoàn toàn từ 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 (chỉ mất vài giây để cung cấp và kết nối với môi trường).
Thao tác này sẽ mở một shell mới ở phần dưới của trình duyệt sau khi có thể hiển thị một quảng cáo xen kẽ giới thiệu.
Sau khi kết nối với Cloud Shell, bạn sẽ thấy rằng mình đã được xác thực và dự án đã đượ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 thiết lập, hãy chạy lệnh sau:
$ gcloud config set project <YOUR_PROJECT_ID>
3. "Hello, World!" có bản đồ
Bắt đầu phát triển bằng bản đồ
Trong Cloud Shell, bạn bắt đầu bằng cách tạo một trang HTML sẽ đóng vai trò là cơ sở cho phần còn lại của lớp học lập trình.
- Trong thanh công cụ của Cloud Shell, hãy nhấp vào Chạy trình chỉnh sửa
để mở một trình chỉnh sửa mã trong thẻ mới.
Trình chỉnh sửa mã dựa trên web này giúp bạn dễ dàng chỉnh sửa các tệp trong Cloud Shell.
- Tạo một thư mục
store-locator
mới cho ứng dụng của bạn trong trình chỉnh sửa mã bằng cách nhấp vào File (Tệp) > New Folder (Thư mục mới).
- Đặ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 đồ.
- Tạo một tệp trong thư mục
store-locator
có tên làindex.html
.
- Đặt nội dung sau 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 đồ. Nội dung này chứa một số CSS để đảm bảo bản đồ chiếm toàn bộ trang một cách trực quan, 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
, chứa tất cả mã JavaScript. Thẻ tập lệnh thứ hai tải khoá API, bao gồm cả việc sử dụng Thư viện Địa điểm cho chức năng tự động hoàn thành mà bạn sẽ thêm sau này và chỉ định tên của hàm JavaScript chạy sau khi Maps JavaScript API được tải, cụ thể là initMap
.
- Thay thế văn bản
YOUR_API_KEY
trong đoạn mã bằng khoá API mà bạn đã tạo trước đó trong lớp học lập trình này. - Cuối cùng, hãy tạo một tệp khác có tên là
app.js
bằng đoạn 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ã tối thiểu cần thiết để tạo bản đồ. Bạn truyền một giá trị tham chiếu đến thẻ <div>
để giữ bản đồ, đồng thời chỉ định tâm và mức thu phóng.
Để kiểm thử ứng dụng này, bạn có thể chạy máy chủ HTTP Python đơn giản trong Cloud Shell.
- Chuyển đến Cloud Shell rồi nhập nội dung sau:
$ cd store-locator $ python3 -m http.server 8080
Bạn sẽ thấy một số dòng đầu ra nhật ký cho biết rằng bạn thực sự đang chạy máy chủ HTTP đơn giản trong Cloud Shell với ứng dụng web đang nghe trên cổng 8080 của localhost.
- 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
trong thanh công cụ Cloud Console rồi chọn Xem trước trên cổng 8080.
Khi bạn nhấp vào mục trình đơn này, một thẻ mới sẽ mở ra trong trình duyệt web của bạn với nội dung HTML được phân phát từ máy chủ HTTP Python đơn giản. Nếu mọi thứ diễn ra suôn sẻ, 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, hãy nhấn Control+C
trong Cloud Shell.
4. Điền dữ liệu vào bản đồ bằng GeoJSON
Bây giờ, hãy xem dữ liệu cho các cửa hàng. GeoJSON là một định dạng dữ liệu thể hiện 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 đối tượng này cũng có thể chứa dữ liệu tuỳ ý. Điều này khiến GeoJSON trở thành lựa chọn phù hợp để biểu thị các cửa hàng, về cơ bản là các điểm trên bản đồ có thêm một chút dữ liệu, chẳng hạn như tên cửa hàng, giờ mở cửa và số điện thoại. Quan trọng nhất là GeoJSON có sự hỗ trợ hàng đầu trong Google Maps, tức là bạn có thể gửi một tài liệu GeoJSON đến một bản đồ trên Google và bản đồ đó sẽ hiển thị tài liệu đó một cách thích hợp.
- Tạo một tệp mới có tên là
stores.json
rồi dán mã sau vào:
stores.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 sau khi xem xét, bạn sẽ thấy đó chỉ là cấu trúc giống nhau được lặp lại cho mỗi cửa hàng. Mỗi cửa hàng được biểu thị dưới dạng Point
GeoJSON cùng với toạ độ và dữ liệu bổ sung có trong khoá properties
. Điều thú vị là GeoJSON cho phép đưa các khoá có tên tuỳ ý vào khoá properties
. Trong lớp học lập trình này, các khoá đó là category
, hours
, description
, name
và phone
.
- Giờ hãy chỉnh sửa
app.js
để tải GeoJSON trongstores.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 lên bản đồ bằng cách gọi loadGeoJson
và truyền tên của tệp JSON. Bạn cũng đã xác định một hàm để chạy mỗi khi người dùng nhấp vào một điểm đánh dấu. Sau đó, hàm này có thể truy cập vào dữ liệu bổ sung cho cửa hàng có điểm đánh dấu được nhấp vào và sử dụng thông tin đó trong một cửa sổ thông tin sẽ xuất hiện. Để kiểm thử ứng dụng này, bạn có thể chạy máy chủ HTTP Python đơn giản bằng cách dùng lệnh tương tự như trước.
- Quay lại Cloud Shell rồi nhập nội dung sau:
$ python3 -m http.server 8080
- Nhấp vào Web Preview (Xem trước trên web)
> Preview on port 8080 (Xem trước trên cổng 8080) một lần nữa và bạn sẽ thấy một bản đồ có đầy đủ các điểm đánh dấu mà bạn có thể nhấp vào để xem thông tin chi tiết về từng cửa hàng, chẳng hạn như ví dụ sau. Tiến bộ!
5. Tuỳ chỉnh bản đồ
Bạn sắp hoàn thành rồi. Bạn có một bản đồ hiển thị tất cả các điểm đánh dấu cửa hàng và thông tin bổ sung khi được nhấp vào. Nhưng nó trông giống như mọi bản đồ khác của Google. Thật tẻ nhạt! Hãy thêm một chút gia vị bằng kiểu bản đồ, điểm đánh dấu, biểu trưng và hình ảnh trong Chế độ xem đường phố tuỳ chỉnh.
Dưới đây là phiên bản mới của app.js
có thêm kiểu tuỳ 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);
});
}
Sau đây là những nội dung bạn đã thêm:
- Biến
mapStyle
chứa tất cả thông tin để tạo kiểu cho bản đồ. (Ngoài ra, bạn còn có thể tạo kiểu riêng nếu muốn.) - Bằng phương thức
map.data.setStyle
, bạn đã áp dụng các điểm đánh dấu tuỳ chỉnh (mỗicategory
có một điểm đánh dấu riêng) từ GeoJSON. - Bạn đã sửa đổi biến
content
để thêm một biểu trưng (vẫn sử dụngcategory
từ GeoJSON) và hình ảnh Chế độ xem đường phố cho vị trí của cửa hàng.
Trước khi triển khai, bạn cần hoàn tất một vài bước:
- Đặt giá trị chính xác cho biến
apiKey
bằng cách thay thế chuỗi'YOUR_API_KEY'
trongapp.js
bằng khoá API của riêng bạn từ trước đó (cũng là khoá mà bạn đã dán vàoindex.html
, giữ nguyên dấu ngoặc kép). - Chạy các lệnh sau trong Cloud Shell để tải đồ hoạ của điểm đánh dấu và biểu trưng xuống. Đảm bảo bạn đang ở trong thư mục
store-locator
. DùngControl+C
để dừng máy chủ HTTP đơn giản nếu máy chủ này đ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
- Xem trước công cụ định vị cửa hàng đã hoàn tất 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 một bản đồ như thế này với kiểu dáng tuỳ chỉnh, hình ảnh điểm đánh dấu tuỳ chỉnh, định dạng cửa sổ thông tin được cải thiện và hình ảnh Chế độ xem đường phố cho từng vị trí:
6. Nhận dữ liệu đầu vào của người dùng
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 địa chỉ nơi họ dự định bắt đầu hành trình. Thêm một thanh tìm kiếm tính năng Tự động hoàn thành địa điểm để 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ập liệu dự đoán 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 tất cả các cụm từ dự đoán đều là Địa điểm trong Google Maps Platform.
- Quay lại bước 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 điều khiển bên liên kết của kết quả. Đừng quên thay thế khoá API nếu bạn dán lê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>
Thanh tìm kiếm Tự động hoàn thành và bảng điều khiển trượt ra ban đầu bị ẩn cho đến khi cần thiết.
- 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
trongapp.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']);
Đoạn mã này hạn chế tính năng Gợi ý tự động chỉ trả về địa chỉ (vì tính năng Tự động hoàn thành địa điểm có thể so khớp tên cơ sở và vị trí hành chính) và giới hạn địa chỉ được trả về chỉ ở Vương quốc Anh. Việc thêm các quy cách không bắt buộc này sẽ giảm số lượng ký tự mà người dùng cần nhập để thu hẹp các kết quả dự đoán nhằm hiển thị địa chỉ mà họ đang tìm kiếm. Sau đó, thao tác 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 những trường cần trả về về từng Địa điểm trong phản hồi.
- Khởi động lại máy chủ và làm mới bản xem trước bằng cách chạy lệnh sau:
$ python3 -m http.server 8080
Giờ đây, bạn sẽ thấy một tiện ích Tự động hoàn thành ở góc trên cùng bên phải của bản đồ, cho biết các địa chỉ ở Vương quốc Anh khớp với nội dung bạn nhập.
Bây giờ, bạn cần xử lý khi người dùng chọn một kết quả dự đoán trong tiện ích Autocomplete (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.
- Thêm mã sau vào cuối
initMap
trongapp.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 nghe để khi người dùng nhấp vào một trong các đề xuất, bản đồ sẽ đặt lại tâm vào địa chỉ đã chọn và đặt vị trí ban đầu làm cơ sở cho các phép tính khoảng cách. Bạn sẽ triển khai các phép tính khoảng cách ở bước tiếp theo.
7. Liệt kê các cửa hàng gần nhất
Directions API hoạt động tương tự như trải nghiệm yêu cầu chỉ đường trong ứng dụng Google Maps – nhập một điểm xuất phát và một điểm đến để nhận được tuyến đường giữa hai điểm đó. Distance Matrix API mở rộng khái niệm này để xác định các cặp tối ưu giữa nhiều điểm khởi hành có thể 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 thấy cửa hàng gần nhất với địa chỉ đã chọn, bạn cung cấp một điểm xuất phát và một mảng vị trí cửa hàng làm điểm đến.
- 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 Distance Matrix API bằng cách sử dụng nguồn gốc được truyền đến hàm dưới dạng một nguồn gốc duy nhất và vị trí cửa hàng dưới dạng một mảng đích đến. Sau đó, hàm này sẽ tạo một mảng các đối tượng lưu trữ mã nhận dạng của cửa hàng, khoảng cách được biểu thị bằng 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 mong muốn thấy danh sách các 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 danh sách trên bảng điều khiển bên cho từng cửa hàng bằ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.
- 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;
}
- Khởi động lại máy chủ và làm mới bản xem trước bằng cách chạy lệnh sau.
$ python3 -m http.server 8080
- Cuối cùng, hãy nhập một đị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 đồ sẽ căn giữa vào địa chỉ đó và một thanh bên sẽ xuất hiện, liệt kê các vị trí cửa hàng theo thứ tự khoảng cách từ địa chỉ đã chọn. Sau đây là một ví dụ minh hoạ:
8. Không bắt buộc: Lưu trữ trang web của bạn
Cho đến thời điểm này, bạn chỉ xem được bản đồ khi đang chạy máy chủ HTTP Python. Để xem bản đồ ngoài phiên Cloud Shell đang hoạt động hoặc để có thể chia sẻ URL của bản đồ với người khác, hãy xem xét việc sử dụng Cloud Storage để lưu trữ trang web của bạn. Cloud Storage là một dịch vụ lưu trữ tệp trực tuyến trên web để 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 các tính năng bảo mật và chia sẻ nâng cao. Ngoài ra, Vercel còn cung cấp một cấp miễn phí, rất 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 bộ chứa, tương tự như các thư mục trên máy tính. Để lưu trữ trang web, trước tiên, bạn cần tạo một vùng chứa. Bạn cần chọn một tên riêng biệt cho nhóm của mình, chẳng hạn như sử dụng tên của bạn làm một phần của tên nhóm.
- Sau khi quyết định chọn 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
là viết tắt của "make bucket" (tạo vùng lưu trữ). Để biết thêm thông tin về tất cả các lệnh có sẵn, kể cả những lệnh bạn sử dụng, hãy xem công cụ gsutil.
Theo mặc định, các bộ chứa và tệp của bạn được lưu trữ trên Cloud Storage ở chế độ riêng tư. Tuy nhiên, đối với công cụ định vị cửa hàng, bạn muốn tất cả các tệp đều ở chế độ công khai để mọi người có thể truy cập vào các tệp đó qua Internet. Bạn có thể chuyển từng tệp sang chế độ công khai sau khi tải lên, nhưng cách này sẽ tốn nhiều thời gian. Thay vào đó, bạn chỉ cần đặt cấp truy cập mặc định cho nhóm mà bạn đã tạo và tất cả các tệp bạn tải lên nhóm đó sẽ kế thừa cấp truy cập đó.
- Chạy lệnh sau, thay thế
yourname-store-locator
bằng tên bạn đã chọn cho nhóm của mình:
$ gsutil defacl ch -u AllUsers:R gs://yourname-store-locator
- Giờ đây, bạn có thể tải tất cả tệp trong thư mục hiện tại lên (hiện tại chỉ có tệp
index.html
vàapp.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 có bản đồ trực tuyến. URL để xem tệp này sẽ là http://storage.googleapis.com/yourname-store-locator/index.html, trong đó yourname-store-locator được thay thế bằng tên nhóm mà bạn đã chọn trước đó.
Dọn dẹp
Cách dễ nhất để dọn dẹp tất 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 khi bắt đầu hướng dẫn này:
- Mở trang Cài đặt trong Bảng điều khiển đám mây
- Nhấp vào Chọn một dự án.
- Chọn dự án mà bạn đã tạo khi bắt đầu hướng dẫn này rồi nhấp vào Open (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 tất lớp học lập trình này.
Kiến thức bạn học được
- Thêm bản đồ có kiểu tuỳ chỉnh bằng Maps JavaScript API
- Tải một lớp dữ liệu lên bản đồ ở định dạng GeoJSON
- Sử dụng Street View Static API để hiển thị hình ảnh Chế độ xem đường phố trong một trang web.
- Sử dụng Thư viện Địa điểm để thêm thanh tìm kiếm Tự động hoàn thành địa điểm vào trang
- Sử dụng dịch vụ Ma trận khoảng cách để tính nhiều khoảng cách bằng một lệnh gọi API duy nhất
- Quản lý và kiểm thử các dự án phát triển web trong Google Cloud Platform bằng giao diện dòng lệnh Cloud Shell dựa trên trình duyệt
- Lưu trữ một trang web bằng Cloud Storage
Tìm hiểu thêm
- Tìm hiểu một cách khác để lưu trữ bản đồ trên web bằng Google App Engine trong lớp học lập trình Lập bản đồ hệ thống tàu điện ngầm của Thành phố New York.
- Khám phá thêm các lớp học lập trình của Nền tảng Google Maps, chẳng hạn như dịch vụ Xây dựng tính năng Tìm kiếm doanh nghiệp lân cận.
- Hãy giúp chúng tôi tạo ra nội dung hữu ích nhất cho bạn bằng cách trả lời câu hỏi bên dưới:
Bạn muốn xem những lớp học lập trình nào khác?
Bạn không thấy khoá học lập trình mà mình muốn trong danh sách trên? Yêu cầu cấp lại bằng cách báo lỗi mới tại đây.
Nếu bạn muốn tìm hiểu thêm về mã, hãy xem kho lưu trữ mã nguồn tại https://github.com/googlecodelabs/google-maps-simple-store-locator.