Google Maps Search Box

  
Hopefully I'm in the right forum...

I have successfully installed Google Maps from the forge, and I'm trying to do something I see no examples for.  I would like to embed Google's own search bar into my map, partially because of the robust type-ahead functionality that it offers.  Instructions on how do this in Javascript are here:

https://developers.google.com/maps/documentation/javascript/examples/places-searchbox

So, if I'm required to use the "initialize" function for google maps to set up the search box, how do I implement that?  I tried using the "AddMapEvent" web block, but there's no "initialize" event that I can listen for.

Thanks in advance.
Alternatively, couldn't you take an address, send it through Geocode to get the Longitude and Latitude and then plot it on the map?
Thanks for your reply.  I could do that, but I would lose the fantastic google places autocomplete that google offers with their search box.  Also, I'd like it floating on top of the map, rather than outside the map.  So yes, you're suggestion is an option if it's not possible to do exactly what I'm looking for.
Solution
Hello, YESCO,

I was trying to come to a working example that didn't rely exclusively on JavaScript, but came to the conclusion that when using our Maps, a necessary library isn't loaded.

We're analysing how to address this in a future release, but if you really want to get this going, why not try to put the JavaScript contents into an Expression, along with the necessary HTML elements to get it to work? (don't forget to set 'Escape Content' to 'No')

I believe this is it:
"<style>
      html, body, #map-canvas {
        min-height: 400px;
        height: 100%;
        margin: 0px;
        padding: 0px
      }
      .controls {
        margin-top: 16px;
        border: 1px solid transparent;
        border-radius: 2px 0 0 2px;
        box-sizing: border-box;
        -moz-box-sizing: border-box;
        height: 32px;
        outline: none;
        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
      }

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

      #pac-input:focus {
        border-color: #4d90fe;
        margin-left: -1px;
        padding-left: 14px;  /* Regular padding-left + 1. */
        width: 401px;
      }

      .pac-container {
        font-family: Roboto;
      }

      #type-selector {
        color: #fff;
        background-color: #4d90fe;
        padding: 5px 11px 0px 11px;
      }

      #type-selector label {
        font-family: Roboto;
        font-size: 13px;
        font-weight: 300;
      }

      /* Fix for corrupted map controls; see this: http://stackoverflow.com/questions/7471830/google-maps-api-v3-weird-ui-display-glitches-with-screenshot */
      .gm-style img { max-width: none; }
      .gm-style label { width: auto; display: inline; }

</style>

<script src='//maps.googleapis.com/maps/api/js?v=3.exp&libraries=places'></script>
<script>
function initialize() {

  var markers = [];
  var map = new google.maps.Map(document.getElementById('map-canvas'), {
    mapTypeId: google.maps.MapTypeId.ROADMAP
  });

  var defaultBounds = new google.maps.LatLngBounds(
      new google.maps.LatLng(-33.8902, 151.1759),
      new google.maps.LatLng(-33.8474, 151.2631));
  map.fitBounds(defaultBounds);

  // Create the search box and link it to the UI element.
  var input = /** @type {HTMLInputElement} */(
      document.getElementById('pac-input'));
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

  var searchBox = new google.maps.places.SearchBox(
    /** @type {HTMLInputElement} */(input));

  // Listen for the event fired when the user selects an item from the
  // pick list. Retrieve the matching places for that item.
  google.maps.event.addListener(searchBox, 'places_changed', function() {
    var places = searchBox.getPlaces();

    if (places.length == 0) {
      return;
    }
    for (var i = 0, marker; marker = markers[i]; i++) {
      marker.setMap(null);
    }

    // For each place, get the icon, place name, and location.
    markers = [];
    var bounds = new google.maps.LatLngBounds();
    for (var i = 0, place; place = places[i]; i++) {
      var image = {
        url: place.icon,
        size: new google.maps.Size(71, 71),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(17, 34),
        scaledSize: new google.maps.Size(25, 25)
      };

      // Create a marker for each place.
      var marker = new google.maps.Marker({
        map: map,
        icon: image,
        title: place.name,
        position: place.geometry.location
      });

      markers.push(marker);

      bounds.extend(place.geometry.location);
    }

    map.fitBounds(bounds);
  });

  // Bias the SearchBox results towards places that are within the bounds of the
  // current map's viewport.
  google.maps.event.addListener(map, 'bounds_changed', function() {
    var bounds = map.getBounds();
    searchBox.setBounds(bounds);
  });
}

google.maps.event.addDomListener(window, 'load', initialize);
</script>

<style>
      #target {
        width: 345px;
      }
</style>
<input id='pac-input' class='controls' type='text' placeholder='Search Box'>
<div id='map-canvas'></div>"
This is nearly all copied from the example, mind you. 
Solution
Carlos,

Not necessarily the news I was hoping to hear, but thank you!  I appreciate the work you've done on this.  We've started to go down this path, but it's such a departure from the Outsystems framework that I think we're looking at going more the route that Gerry suggested.  If you have any more developments on this however, please let me know, I'd love to stay updated.
Gerry wrote:
Alternatively, couldn't you take an address, send it through Geocode to get the Longitude and Latitude and then plot it on the map?
 Gerry how do you go about having a search bar, taking that address and geocoding it with google maps? i'm fairly new to outsystems so bear with me if you can. 
 
Hi Carlos

Thank you for your working Outsystems version of  https://developers.google.com/maps/documentation/javascript/examples/places-searchbox

Any tips how to convert https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete to work within an outsystems context.

  • I get it to work in jsfiddle
  • I removed the double quotes and replaced with single quotes
  • What do we need to know about conversion other than learn JavaScript you dummy!

"<style>
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#map {
  height: 100%;
}

.controls {
  margin-top: 10px;
  border: 1px solid transparent;
  border-radius: 2px 0 0 2px;
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  height: 32px;
  outline: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

#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: 300px;
}

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

.pac-container {
  font-family: Roboto;
}

#type-selector {
  color: #fff;
  background-color: #4d90fe;
  padding: 5px 11px 0px 11px;
}

#type-selector label {
  font-family: Roboto;
  font-size: 13px;
  font-weight: 300;
}
      /* Fix for corrupted map controls; see this: http://stackoverflow.com/questions/7471830/google-maps-api-v3-weird-ui-display-glitches-with-screenshot */
      .gm-style img { max-width: none; }
      .gm-style label { width: auto; display: inline; }

</style>


<script src='//maps.googleapis.com/maps/api/js?v=3.exp&key=REPLACE_WITH_YOUR_KEY_WHICH_I_HAVE_DONE&libraries=places'></script>
<script>

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    center: {
      lat: -33.8688,
      lng: 151.2195
    },
    zoom: 13
  });
  var input = /** @type {!HTMLInputElement} */ (document.getElementById('pac-input'));
  var types = document.getElementById('type-selector');
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(types);
  var autocomplete = new google.maps.places.Autocomplete(input);
  autocomplete.bindTo('bounds', map);
  var infowindow = new google.maps.InfoWindow();
  var marker = new google.maps.Marker({
    map: map,
    anchorPoint: new google.maps.Point(0, -29)
  });
  autocomplete.addListener('place_changed', function() {
    infowindow.close();
    marker.setVisible(false);
    var place = autocomplete.getPlace();
    if (!place.geometry) {
      window.alert('Autocomplete returned place contains no geometry');
      return;
    }
    if (place.geometry.viewport) {
      map.fitBounds(place.geometry.viewport);
    } else {
      map.setCenter(place.geometry.location);
      map.setZoom(17);
    }
    marker.setIcon( /** @type {google.maps.Icon} */ ({
      url: place.icon,
      size: new google.maps.Size(71, 71),
      origin: new google.maps.Point(0, 0),
      anchor: new google.maps.Point(17, 34),
      scaledSize: new google.maps.Size(35, 35)
    }));
    marker.setPosition(place.geometry.location);
    marker.setVisible(true);
    var address = '';
    if (place.address_components) {
      address = [(place.address_components[0] && place.address_components[0].short_name || ''), (place.address_components[1] && place.address_components[1].short_name || ''), (place.address_components[2] && place.address_components[2].short_name || '')].join(' ');
    }
    infowindow.setContent('<div><strong>' + place.name + '</strong><br>' + address);
    infowindow.open(map, marker);
  });

  function setupClickListener(id, types) {
    var radioButton = document.getElementById(id);
    radioButton.addEventListener('click', function() {
      autocomplete.setTypes(types);
    });
  }
  setupClickListener('changetype-all', []);
  setupClickListener('changetype-address', ['address']);
  setupClickListener('changetype-establishment', ['establishment']);
  setupClickListener('changetype-geocode', ['geocode']);
}

</script>

<input id='pac-input' class='controls' type='text' placeholder='Enter a location'>
<div id='type-selector' class='controls'>
  <input type='radio' name='type' id='changetype-all' checked='checked'>
  <label for='changetype-all'>All</label>
  <input type='radio' name='type' id='changetype-establishment'>
  <label for='changetype-establishment'>Establishments</label>
  <input type='radio' name='type' id='changetype-address'>
  <label for='changetype-address'>Addresses</label>
  <input type='radio' name='type' id='changetype-geocode'>
  <label for='changetype-geocode'>Geocodes</label>
</div>
<div id='map'></div>"
Many thanks in advance

Hey, George,

Well, as it turns out, just a little bit of JavaScript (and CSS) was needed to get your snippet to work: you needed to call the "initMap" function so the map initialization would occur, along with the radio button logic. Just add "google.maps.event.addDomListener(window, 'load', initMap);" right before the closing </script> tag. 

Then you'll have everything initialized, but the map container with 0 pixels of height. I'd change the #map class to something like this:

#map {
  min-height: 500px;
  width: 100%;
}

Then you'll have your example working! But if you're wondering how to get it working with a "Map" block from the component itself, then check out the attached OML (which also includes your code with my changes, along with the one you saw above).


Is this what you were looking for?


Best regards,

Carlos Simões

Many thanks and that was exactly what I was after. Will have a look at the OML.

Google make lovely api!