You're all set!

To start developing, please head over to our developer documentation.

Activate the Google Maps JavaScript API

To get you started we'll guide you through the Google Developers Console to do a few things first:

  1. Create or choose a project
  2. Activate the Google Maps JavaScript API and related services
  3. Create appropriate keys
Continue

Creating a Store Locator on Google Maps

Overview

This tutorial shows you how to build a Google Maps application for your website that allows your users to search for store locations that are nearest to them. The tutorial suits people with intermediate knowledge of HTML, JavaScript, PHP and MySQL.

The screenshot below displays an example of a store locator application for fictional clothing stores. A database in MySQL stores information about individual clothing retail stores, like the store name, address and geographic coordinates. The map uses an XML file that acts as an intermediary between the database and the map. You can use PHP statements to export marker information from the database to the XML file.

When the user enters a location in the Search location field, selects a search Radius, and the clicks the Search button, the map retrieves information from the database, and adds markers to the map. A user can click the markers to display info windows with store information, and also click the See all results drop down list to view a list of all stores in the search radius.

The sample below shows the client-side code you need to create a store locator application. Hover at top right of the code block to copy the code.

<!DOCTYPE html >
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <title>Creating a Store Locator on Google Maps</title>
  <style>
    /* Always set the map height explicitly to define the size of the div
     * element that contains the map. */
    #map {
      height: 100%;
    }
    /* Optional: Makes the sample page fill the window. */
    html, body {
      height: 100%;
      margin: 0;
      padding: 0;
    }
 </style>
  </head>
  <body style="margin:0px; padding:0px;" onload="initMap()">
    <div>
         <label for="raddressInput">Search location:</label>
         <input type="text" id="addressInput" size="15"/>
        <label for="radiusSelect">Radius:</label>
        <select id="radiusSelect" label="Radius">
          <option value="50" selected>50 kms</option>
          <option value="30">30 kms</option>
          <option value="20">20 kms</option>
          <option value="10">10 kms</option>
        </select>

        <input type="button" id="searchButton" value="Search"/>
    </div>
    <div><select id="locationSelect" style="width: 10%; visibility: hidden"></select></div>
    <div id="map" style="width: 100%; height: 90%"></div>
    <script>
      var map;
      var markers = [];
      var infoWindow;
      var locationSelect;

        function initMap() {
          var sydney = {lat: -33.863276, lng: 151.107977};
          map = new google.maps.Map(document.getElementById('map'), {
            center: sydney,
            zoom: 11,
            mapTypeId: 'roadmap',
            mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU}
          });
          infoWindow = new google.maps.InfoWindow();

          searchButton = document.getElementById("searchButton").onclick = searchLocations;

          locationSelect = document.getElementById("locationSelect");
          locationSelect.onchange = function() {
            var markerNum = locationSelect.options[locationSelect.selectedIndex].value;
            if (markerNum != "none"){
              google.maps.event.trigger(markers[markerNum], 'click');
            }
          };
        }

       function searchLocations() {
         var address = document.getElementById("addressInput").value;
         var geocoder = new google.maps.Geocoder();
         geocoder.geocode({address: address}, function(results, status) {
           if (status == google.maps.GeocoderStatus.OK) {
            searchLocationsNear(results[0].geometry.location);
           } else {
             alert(address + ' not found');
           }
         });
       }

       function clearLocations() {
         infoWindow.close();
         for (var i = 0; i < markers.length; i++) {
           markers[i].setMap(null);
         }
         markers.length = 0;

         locationSelect.innerHTML = "";
         var option = document.createElement("option");
         option.value = "none";
         option.innerHTML = "See all results:";
         locationSelect.appendChild(option);
       }

       function searchLocationsNear(center) {
         clearLocations();

         var radius = document.getElementById('radiusSelect').value;
         var searchUrl = 'storelocator.php?lat=' + center.lat() + '&lng=' + center.lng() + '&radius=' + radius;
         downloadUrl(searchUrl, function(data) {
           var xml = parseXml(data);
           var markerNodes = xml.documentElement.getElementsByTagName("marker");
           var bounds = new google.maps.LatLngBounds();
           for (var i = 0; i < markerNodes.length; i++) {
             var id = markerNodes[i].getAttribute("id");
             var name = markerNodes[i].getAttribute("name");
             var address = markerNodes[i].getAttribute("address");
             var distance = parseFloat(markerNodes[i].getAttribute("distance"));
             var latlng = new google.maps.LatLng(
                  parseFloat(markerNodes[i].getAttribute("lat")),
                  parseFloat(markerNodes[i].getAttribute("lng")));

             createOption(name, distance, i);
             createMarker(latlng, name, address);
             bounds.extend(latlng);
           }
           map.fitBounds(bounds);
           locationSelect.style.visibility = "visible";
           locationSelect.onchange = function() {
             var markerNum = locationSelect.options[locationSelect.selectedIndex].value;
             google.maps.event.trigger(markers[markerNum], 'click');
           };
         });
       }

       function createMarker(latlng, name, address) {
          var html = "<b>" + name + "</b> <br/>" + address;
          var marker = new google.maps.Marker({
            map: map,
            position: latlng
          });
          google.maps.event.addListener(marker, 'click', function() {
            infoWindow.setContent(html);
            infoWindow.open(map, marker);
          });
          markers.push(marker);
        }

       function createOption(name, distance, num) {
          var option = document.createElement("option");
          option.value = num;
          option.innerHTML = name;
          locationSelect.appendChild(option);
       }

       function downloadUrl(url, callback) {
          var request = window.ActiveXObject ?
              new ActiveXObject('Microsoft.XMLHTTP') :
              new XMLHttpRequest;

          request.onreadystatechange = function() {
            if (request.readyState == 4) {
              request.onreadystatechange = doNothing;
              callback(request.responseText, request.status);
            }
          };

          request.open('GET', url, true);
          request.send(null);
       }

       function parseXml(str) {
          if (window.ActiveXObject) {
            var doc = new ActiveXObject('Microsoft.XMLDOM');
            doc.loadXML(str);
            return doc;
          } else if (window.DOMParser) {
            return (new DOMParser).parseFromString(str, 'text/xml');
          }
       }

       function doNothing() {}
  </script>
    <script async defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
    </script>
  </body>
</html>

Getting started

Install, set up and configure a MySQL server with PHP on your machine.

Creating a table in MySQL

Create a table in MySQL containing attributes of the markers on the map, like the marker id, name, address, lat, and lng. The id attribute serves as the primary key.

To keep the storage space for your table at a minimum, you can specify the lat and lng attributes to be floats of size (10,6). This allows the fields to store 6 digits after the decimal, plus up to 4 digits before the decimal.

You can interact with the MySQL datbase through the phpMyAmin interface. The screenshot below displays the table setup in phpMyAdmin.

You can also use SQL commands to create the table, as in the SQL statement below.

CREATE TABLE `markers` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  `name` VARCHAR( 60 ) NOT NULL ,
  `address` VARCHAR( 80 ) NOT NULL ,
  `lat` FLOAT( 10, 6 ) NOT NULL ,
  `lng` FLOAT( 10, 6 ) NOT NULL
) ENGINE = MYISAM ;

Populating the table

You can import the marker data into the SQL database using the 'Import' functionality of the phpMyAdmin interface which allows you to import data in various formats.

Below is the marker data for the map in this tutorial, in the .csv format.

1,Heir Apparel,"Crowea Pl, Frenchs Forest NSW 2086",-33.737885,151.235260
2,BeeYourself Clothing,"Thalia St, Hassall Grove NSW 2761",-33.729752,150.836090
3,Dress Code,"Glenview Avenue, Revesby, NSW 2212",-33.949448,151.008591
4,The Legacy,"Charlotte Ln, Chatswood NSW 2067",-33.796669,151.183609
5,Fashiontasia,"Braidwood Dr, Prestons NSW 2170",-33.944489,150.854706
6,Trish & Tash,"Lincoln St, Lane Cove West NSW 2066",-33.812222,151.143707
7,Perfect Fit,"Darley Rd, Randwick NSW 2031",-33.903557,151.237732
8,Buena Ropa!,"Brodie St, Rydalmere NSW 2116",-33.815521php,151.026642
9,Coxcomb and Lily Boutique,"Ferrers Rd, Horsley Park NSW 2175",-33.829525,150.873764
10,Moda Couture,"Northcote Rd, Glebe NSW 2037",-33.873882,151.177460

You can also use the SQL commands below to import the marker data into the SQL table.

INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('1','Heir Apparel','Crowea Pl, Frenchs Forest NSW 2086','-33.737885','151.235260');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('2','BeeYourself Clothing','Thalia St, Hassall Grove NSW 2761','-33.729752','150.836090');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('3','Dress Code','Glenview Avenue, Revesby, NSW 2212','-33.949448','151.008591');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('4','The Legacy','Charlotte Ln, Chatswood NSW 2067','-33.796669','151.183609');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('5','Fashiontasia','Braidwood Dr, Prestons NSW 2170','-33.944489','150.854706');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('6','Trish & Tash','Lincoln St, Lane Cove West NSW 2066','-33.812222','151.143707');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('7','Perfect Fit','Darley Rd, Randwick NSW 2031','-33.903557','151.237732');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('8','Buena Ropa!','Brodie St, Rydalmere NSW 2116','-33.815521','151.026642');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('9','Coxcomb and Lily Boutique','Ferrers Rd, Horsley Park NSW 2175','-33.829525','150.873764');
INSERT INTO `markers` (`id`, `name`, `address`, `lat`, `lng`) VALUES ('10','Moda Couture','Northcote Rd, Glebe NSW 2037','-33.873882','151.177460');

Outputting data as XML using PHP

At this point, you should have a table named markers containing the map marker data. This section shows you how to export the table data from the SQL database in an XML format, using PHP statements. The map can use the XML file to retrieve the marker data through asynchronous JavaScript calls.

Using an XML file as an intermediary between your database and your Google map allows for faster initial page load, and a more flexible map application. It makes debugging easier as you can independently verify the XML output from the database, and the JavaScript parsing of the XML. You can also run the map entirely based on static XML files only, and not use the MySQL database.

If you have never used PHP to connect to a MySQL database, visit php.net and read up on mysql_connect, mysql_select_db, my_sql_query, and mysql_error.

When using a public browser to access a database using PHP files, it's important to ensure that your database credentials are secure. You can do this by putting your database connection information in a separate PHP file to that of the main PHP code.

Create a new file in a text editor and save it as phpsqlsearch_dbinfo.php. The file with your credentials should look like the one below, but containing your own database information.

<?php
$username="username";
$password="password";
$database="username-databaseName";
?>

Finding locations with MySQL

To find locations in your markers table that are within a certain radius distance of a given latitude/longitude, you can use a SELECT statement based on the Haversine formula. The Haversine formula is used generally for computing great-circle distances between two pairs of coordinates on a sphere. An in-depth mathemetical explanation is given by Wikipedia and a good discussion of the formula as it relates to programming is on the Movable Type Scripts website.

Here's the SQL statement that finds the closest 20 locations within a radius of 25 miles to the -33, 151 coordinate. It calculates the distance based on the latitude/longitude of that row and the target latitude/longitude, and then asks for only rows where the distance value is less than 25, orders the whole query by distance, and limits it to 20 results. To search by kilometers instead of miles, replace 3959 with 6371.

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;

Using PHP's DOM XML functions to output XML

The DOM XML functions of PHP take care of subtleties such as escaping special entities in the XML, and make it easy to create XML with more complex structures. You can use DOM XML functions to create XML nodes, append child nodes, and output an XML document to the screen. To determine if your server's PHP has the DOM XML functionality enabled, check your configuration or try initializing a domxml_new_doc().

Create a new file in a text editor and save it as storelocator.php. Copy the code below to the storelocator.php file to connect to the MySQL database, and dump the XML to the browser.

<?php
require("phpsqlsearch_dbinfo.php");
// Get parameters from URL
$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];
// Start XML file, create parent node
$dom = new DOMDocument("1.0");
$node = $dom->createElement("markers");
$parnode = $dom->appendChild($node);
// Opens a connection to a mySQL server
$connection=mysql_connect (localhost, $username, $password);
if (!$connection) {
  die("Not connected : " . mysql_error());
}
// Set the active mySQL database
$db_selected = mysql_select_db($database, $connection);
if (!$db_selected) {
  die ("Can\'t use db : " . mysql_error());
}
// Search the rows in the markers table
$query = sprintf("SELECT id, name, address, lat, lng, ( 3959 * acos( cos( radians('%s') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < '%s' ORDER BY distance LIMIT 0 , 20",
  mysql_real_escape_string($center_lat),
  mysql_real_escape_string($center_lng),
  mysql_real_escape_string($center_lat),
  mysql_real_escape_string($radius));
$result = mysql_query($query);
$result = mysql_query($query);
if (!$result) {
  die("Invalid query: " . mysql_error());
}
header("Content-type: text/xml");
// Iterate through the rows, adding XML nodes for each
while ($row = @mysql_fetch_assoc($result)){
  $node = $dom->createElement("marker");
  $newnode = $parnode->appendChild($node);
  $newnode->setAttribute("id", $row['id']);
  $newnode->setAttribute("name", $row['name']);
  $newnode->setAttribute("address", $row['address']);
  $newnode->setAttribute("lat", $row['lat']);
  $newnode->setAttribute("lng", $row['lng']);
  $newnode->setAttribute("distance", $row['distance']);
}
echo $dom->saveXML();
?>

In the file above, the PHP code first initializes a new XML document and creates the "markers" parent node. It then connects to the database, executes a SELECT * (select all) query on the markers table, and iterates through the results. For each row in the table (each location), the code creates a new XML node with the row attributes as XML attributes, and appends it to the parent node. The last part of code then dumps the XML to the browser screen.

Notes:

  • Since this PHP sends user input in a MySQL statement, this code uses the mysql_real_escape_string technique of avoiding SQL injection. A more elegant solution may be to use MySQL prepared statements, though implementation varies on the PHP version and DB wrapper used.
  • If your database contains international characters or you otherwise need to force a UTF-8 output, you can use utf8_encode on the data output.

Checking that XML output works

To confirm that the PHP script is producing valid XML, call the php script file you created from your browser to see the XML output as below. Append reasonable sample query parameters to the URL (for example, ?lat=-33&lng=1512&radius=100), and you should see XML output like the code below.

<?xml version="1.0"?>
<markers>
<marker id="1" name="Heir Apparel" address="Crowea Pl, Frenchs Forest NSW 2086" lat="-33.737885" lng="151.235260" distance="52.762480400236754"/>
<marker id="2" name="BeeYourself Clothing" address="Thalia St, Hassall Grove NSW 2761" lat="-33.729752" lng="150.836090" distance="51.30359905145628"/>
<marker id="3" name="Dress Code" address="Glenview Avenue, Revesby, NSW 2212" lat="-33.949448" lng="151.008591" distance="65.60640686758967"/>
<marker id="4" name="The Legacy" address="Charlotte Ln, Chatswood NSW 2067" lat="-33.796669" lng="151.183609" distance="56.05760641917"/>
<marker id="5" name="Fashiontasia" address="Braidwood Dr, Prestons NSW 2170" lat="-33.944489" lng="150.854706" distance="65.79696354609702"/>
<marker id="6" name="Trish & Tash" address="Lincoln St, Lane Cove West NSW 2066" lat="-33.812222" lng="151.143707" distance="56.731386263386064"/>
<marker id="7" name="Perfect Fit" address="Darley Rd, Randwick NSW 2031" lat="-33.903557" lng="151.237732" distance="63.92017421824136"/>
<marker id="8" name="Buena Ropa!" address="Brodie St, Rydalmere NSW 2116" lat="-33.815521" lng="151.026642" distance="56.37149740204364"/>
<marker id="9" name="Coxcomb and Lily Boutique" address="Ferrers Rd, Horsley Park NSW 2175" lat="-33.829525" lng="150.873764" distance="57.77872495434665"/>
<marker id="10" name="Moda Couture" address="Northcote Rd, Glebe NSW 2037" lat="-33.873882" lng="151.177460" distance="61.243992108021324"/>
</markers>

If your browser isn't displaying the marker data from your database as an XML output, try debugging by removing the line in the file that sets the header to the text/xml content type. This line may cause your browser to try to parse XML and make it difficult to see your debugging messages.

Creating the map

This section shows you how to develop the map example in this tutorial using JavaScript, and the output XML file. You can learn more the basics of creating a Google Map by reading the tutorial about creating a map with a marker..

Create a new file in a text editor and save it as index.html. You can find the full sample code that creates the map in this tutorial at the beginning of this page. Copy the entire code and paste it in the index.html file.

The sections that follow explain the code that creates the map.

that youRead the sections that follow to understand the code that you can add to this file.

Setting up the controls

This section explains the code that sets up the following controls on the map:

  • Search Near user input field
  • Radius drop down list
  • Search button
  • See all results drop down list of search results

You can customize the layout of the map to allow your store locator application to display nicely in a mobile browser. Instead of using a sidebar to display a list of all the search results, you can use a dropdown menu which displays the store locations in a large, easy-to-read list. You can set the CSS width of your dropdown menu and map to be 100% to ensure that it fills the mobile screen space.

 <div><select id="locationSelect" style="width:100%;visibility:hidden"></select></div>
 <div id="map" style="width: 100%; height: 80%"></div>

Finding stores near a user's search location

When a user enters a location in the Search field, the PHP script requires latitude and longitude parameters in order to perform the search. The map script uses the Geocoder class to turn the user-entered location or address into a latitude and longitude coordinates.

The code below attaches the Search button to a searchLocations function, which passes the user-entered location/address from the Search field to the asynchronous geocoder.geocode function. If the geocode was successful, and the geocoder.geocode function gets a LatLng in response, it sends the coordinates to the searchLocationsNear function.

function searchLocations() {
  var address = document.getElementById("addressInput").value;
  var geocoder = new google.maps.Geocoder();
  geocoder.geocode({address: address}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      searchLocationsNear(results[0].geometry.location);
    } else {
      alert(address + ' not found');
    }
  });
}

Loading the XML results

The code passes the geographical coordinates of the location the user entered in the Search Near field, and passes it to the PHP script to process the XML output. Use the browser-provided XMLHttpRequest object to load the XML file into the page. This object lets you retrieve a file that resides on the same domain as the requesting web page, and is the basis of AJAX programming.

Define your own function for loading the file, and call it downloadUrl(). The code below dislays the url parameter which specifies the path to the PHP script. It is beneficial to have this reside in the same directory as the HTML so that you can just refer to it by filename.

function downloadUrl(url,callback) {
 var request = window.ActiveXObject ?
     new ActiveXObject('Microsoft.XMLHTTP') :
     new XMLHttpRequest;

 request.onreadystatechange = function() {
   if (request.readyState == 4) {
     request.onreadystatechange = doNothing;
     callback(request.responseText, request.status);
   }
 };

 request.open('GET', url, true);
 request.send(null);
}

Since the XMLHttpRequest is asynchronous, the callback parameter initiates the downloadURL function based on the size of the XML file. The bigger your XML file, the longer it may take. For this reason, it is best not to put any code after the downloadUrl function that relies on the markers inside the callback function. Instead, you can put such code inside the callback function.

Now that you have defined the function, you can call it from your code, passing in the storelocator.php file and callback function. The map in this tutorial calls a static XML file for the marker data, as in the code below.


function searchLocationsNear(center) {
  clearLocations();

  var radius = document.getElementById('radiusSelect').value;
  var searchUrl = 'storelocator.php?lat=' + center.lat() + '&lng=' + center.lng() + '&radius=' + radius;
  downloadUrl(searchUrl, function(data) {
  var xml = parseXml(data);
  var markerNodes = xml.documentElement.getElementsByTagName("marker");
  var bounds = new google.maps.LatLngBounds();
  for (var i = 0; i < markerNodes.length; i++) {
    var id = markerNodes[i].getAttribute("id");
    var name = markerNodes[i].getAttribute("name");
    var name = markerNodes[i].getAttribute("name");
    var address = markerNodes[i].getAttribute("address");
    var distance = parseFloat(markerNodes[i].getAttribute("distance"));
    var latlng = new google.maps.LatLng(
        parseFloat(markerNodes[i].getAttribute("lat")),
        parseFloat(markerNodes[i].getAttribute("lng")));

    createOption(name, distance, i);
    createMarker(latlng, name, address);
    bounds.extend(latlng);
  }
  map.fitBounds(bounds);
 });
}

For each marker element, the code above retrieves the id, name, address, distance, and lat/lng attributes, and passes them to createMarker() function to create the marker, and to the createOption() function to create a result in the See all results drop down. You can use the LatLngBounds.extend and Map.fitBounds functions to calculate the optimal viewport for the results.

Creating markers and the sidebar

The code below creates a marker at a LatLng using the createMarker() function, and adds an event listener to the marker that displays an info window when the user clicks a marker.

function createMarker(latlng, name, address) {
  var html = "<b>" + name + "</b> <br/>" + address;
  var marker = new google.maps.Marker({
    map: map,
    position: latlng
  });
  google.maps.event.addListener(marker, 'click', function() {
    infoWindow.setContent(html);
    infoWindow.open(map, marker);
  });
  markers.push(marker);
}

In the code below, the createOption() function creates an option element that displays the name of the store. The value of the option is the index of the marker in the global markers array. You can use this value to open an info window over the marker when the user selects a corresponding option on the 'See all results' drop down list.

function createOption(name, distance, num) {
  var option = document.createElement("option");
  option.value = num;
  option.innerHTML = name;
  locationSelect.appendChild(option);
}

Clearing previous results

In the code below, the clearLocations() function sets the map property to null in order to clear any existing markers, info windows and drop down lists between searches.

function clearLocations() {
  infoWindow.close();
  for (var i = 0; i < markers.length; i++) {
    markers[i].setMap(null);
  }
  markers.length = 0;

  locationSelect.innerHTML = "";
  var option = document.createElement("option");
  option.value = "none";
  option.innerHTML = "See all results:";
  locationSelect.appendChild(option);
  locationSelect.style.visibility = "visible";
}

Putting it all together

Open the index.html file in a browser. When the page loads, the initMap function sets up the map and then calls the downloadUrl function. This function iterates through all the marker elements, and retrieves the name, address, type, and latLng attributes for each marker element.
The code then sets up the map. The user can use the 'Search Near' field to enter a location name, and select a search radius to display markers on the map. The map also binds info windows to each marker that displays the store name and address.

More information

If you want to plot markers on a map using your own location data, you can use a batch geocoding service to convert addresses into latitudes and longitudes for your markers.

Now that you have a store locator for your website, consider extending it with more features.

  • User location: The Android phone provides a Gears Geolocation module for determining the approximate location of a user, and the iPhone supports the W3C geolocation property. You can use this information to do a default search for the user when they first arrive at your page, and minimize the amount of typing they need to do. See this example.
  • Advanced search: Let users restrict searches by category or some attribute. Check out the Ace Hardware store locator for a great example of this.
  • Richer info windows: You can provide more information in your info windows by adding quick links for the user to zoom into a location, or provide get driving directions to or from a location.

Gửi phản hồi về...

Google Maps JavaScript API
Google Maps JavaScript API
Bạn cần trợ giúp? Truy cập trang hỗ trợ của chúng tôi.