Highlight a area with Google Maps JavaScript API v3 - google-maps-api-3

I want to highlight a area like on the image below which is taken from Google Maps. Is this possible to accomplish with the current version of their API (v3)? If yes, how?
Thanks in advance.

You need to know the vertices of the area and create a polygon based on them.
But dashed strokes currently are not supported by polygons, if you require to have a dashed stroke you must create a sequence of polylines with different stroke-colors based on the vertices.
A built-in method to highlight an area currently doesn't exist.

You can automatically highlight an area with Google Maps Javascript API, here is an example:
<!DOCTYPE html>
<html>
<head>
<title>Place ID Finder</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
#map {
height: 100%;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#ggg
{
position: absolute;
left:20%;
top:5%;
}
.controls {
background-color: #fff;
border-radius: 2px;
border: 1px solid transparent;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
box-sizing: border-box;
font-family: Roboto;
font-size: 15px;
font-weight: 300;
height: 29px;
margin-left: 17px;
margin-top: 10px;
outline: none;
padding: 0 11px 0 13px;
text-overflow: ellipsis;
width: 400px;
}
.controls:focus {
border-color: #4d90fe;
}
.title {
font-weight: bold;
}
#infowindow-content {
display: none;
}
#map #infowindow-content {
display: inline;
}
</style>
</head>
<body>
<div style="display: none">
<input id="pac-input" class="controls" type="text" placeholder="Enter a location">
</div>
<div id="map"></div>
<div id="infowindow-content">
<span id="place-name" class="title"></span><br>
<strong>Place ID:</strong> <span id="place-id"></span><br>
<span id="place-address"></span>
</div>
<script>
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 37.9989746, lng: 23.6413698},
zoom: 11
});
var input = document.getElementById('pac-input');
var autocomplete = new google.maps.places.Autocomplete(input);
autocomplete.bindTo('bounds', map);
autocomplete.setFields(
['place_id', 'geometry', 'name', 'formatted_address']);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
var infowindow = new google.maps.InfoWindow();
var infowindowContent = document.getElementById('infowindow-content');
infowindow.setContent(infowindowContent);
var marker = new google.maps.Marker({map: map});
marker.addListener('click', function() {
infowindow.open(map, marker);
});
autocomplete.addListener('place_changed', function() {
infowindow.close();
var place = autocomplete.getPlace();
if (!place.geometry) {
return;
}
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(17);
}
marker.setPlace({
placeId: place.place_id,
location: place.geometry.location,
});
marker.setVisible(true);
infowindowContent.children['place-name'].textContent = place.name;
infowindowContent.children['place-id'].textContent = place.place_id;
infowindowContent.children['place-address'].textContent = place.formatted_address;
infowindow.open(map, marker);
var frame = document.getElementById("map");
frame.innerHTML = '<iframe width="100%" height="100%" frameborder="0" style="border:0" src="https://www.google.com/maps/embed/v1/place?q=place_id:'.concat(place.place_id).concat('&key=XXXXXXX" allowfullscreen></iframe>');
frame.innerHTML += '<p id="ggg"><button type="button" onclick="location.reload()">Try again!</button></p>';
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=XXXXXXXXX&libraries=places&callback=initMap" async defer></script>
</body>
</html>
You will need to replace API KEY with your API KEY. Try e.g."Nea Smyrni 171 21, Greece" in the input and then select the first drop down option and notice that it automatically highlights the area!

Related

How to show Captions in an external container using JWPlayer v8

Our videos use the lower third of the page for introductions, etc. much like TV News stations do. When captions are on, they're blocking all of that, thus creating a LOT of complaints from the communities that need the captions. I've tried tinkering with the CSS, but with a responsive layout, resizing the player wreaks havoc, often putting them out of sight altogether.
Is there a setting that can be changed, or technique to use, that will keep the captions at the top and in view when resized, OR in an external container?
Problem: the JW player 608 live captions are not formatted cleanly.
To solve this, disable the JW caption display and format our own window, named "ccbuffer"
<style type="text/css">
.jw-captions {
display: none !important;
}
#ccbuffer {
border: 2px solid white !important;
border-radius: 4px;
background-color: black !important;
display: flex;
height: 120px;
margin-top: 6px;
font: 22px bold arial, sans-serif;
color: white;
justify-content: center;
align-items: center;
}
</style>
Here is where I show the player, and ccbuffer is a div right below it
<div id="myPlayer">
<p style="color: #FFFFFF; font-weight: bold; font-size: x-large; border-style: solid; border-color: #E2AA4F">
Loading video...
</p>
</div>
<div id="ccbuffer" />
DOMSubtreeModified is deprecated. Use MutationObserver, which is less stressful on the client.
Let's hook the 'captionsChanged' event from JW. if track is 0 then no captions are selected and we disconnect the observer. If captions are selected, then we use jquery to pull the text out of the jw-text-track-cue element, and format it into a nice 3 line display in our ccbuffer window.
<script>
var observer;
jwplayer().on('captionsChanged', function (event) {
if (event.track == 0) {
observer.disconnect();
$('#ccbuffer').hide('slow');
}
else {
$('#ccbuffer').show('slow');
// select the target node
var target = document.querySelector('.jw-captions');
// create an observer instance
observer = new MutationObserver(function(mutations) {
$('.jw-text-track-cue').each(function(i) {
if (i == 0)
$('#ccbuffer').html( $(this).text() );
else
$('#ccbuffer').append("<br/>" + $(this).text() );
});
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true }
// pass in the target node, as well as the observer options
observer.observe(target, config);
}
});
$(document).ready(function () {
$('#ccbuffer').hide();
});
</script>
So when the user enables captions, the ccbuffer window will slide open and display a clean 3 line representation of the CC text.
Final Solution: External Captions that are draggable/resizable
All credit to #Basildane, I worked out how to extenalize the captions with VOD, and to make them draggable and resizable, with CSS experimentation for ADA consideration:
<!DOCTYPE html>
<html>
<head>
<title>JW External Captions</title>
<meta http-equiv="Expires" content="Fri, Jan 01 1900 00:00:00 GMT">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Lang" content="en">
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="/jwplayer/v8.10/jwplayer.js"></script>
<style type="text/css">
#myPlayer {
margin-bottom:5px;
}
.jw-captions {
display: none !important;
}
#ccbuffer {
color: white;
background-color: black;
opacity:.7;
font: 22px bold san-serif;
width: 100%;
padding: 15px;
height: 100%;
position:relative;
}
.night {
color:silver !important;
background-color: black !important;
opacity:1 !important;
border-color:silver !important;
}
.highcontrast {
color:white ! important;
background-color: black !important;
opacity:1 !important;
border-color:white !important;
}
.highcontrast2 {
color:black !important;
background-color: yellow !important;
opacity:1 !important;
border-color:black !important;
}
.highcontrast3 {
color:yellow !important;
background-color: black !important;
opacity:1 !important;
border-color:yellow !important;
}
#ccContainer {
position: absolute;
z-index: 9;
border: 1px solid inherit;
overflow: hidden;
resize: both;
width: 640px;
height: 180px;
min-width: 120px;
min-height: 90px;
max-width: 960px;
max-height: 300px;
}
#ccContainerheader {
padding: 3px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
border:1px solid;
}
</style>
</head>
<body>
<h3>JW Draggable Captions Container</h3>
<div id="PlayerContainer" style="width:401px;">
<div id="myPlayer">Loading video...</div>
</div>
<div id="ccContainer">
<!-- Include a header DIV with the same name as the draggable DIV, followed by "header" -->
<div style="float:right;">
<form id="myform">
<select id="ccFontFamily">
<option value="sans-serif">Default Font</option>
<option value="serif">Serif</option>
<option value="monospace">Monospace</option>
<option value="cursive">Cursive </option>
</select>
<select id="ccFontSize" style="">
<option value="22">Default Size</option>
<option value="14">14</option>
<option value="18">18</option>
<option value="24">24</option>
<option value="32">32</option>
</select>
<select id="ccContrast" style="">
<option value="ccdefault">Default Contrast</option>
<option value="night">Night</option>
<option value="highcontrast">High Contrast</option>
<option value="highcontrast2">Black/Yellow</option>
<option value="highcontrast3">Yellow/Black</option>
</select>
<button id="ccFontReset">Reset</button>
</form>
</div>
<div id="ccContainerheader">
Captions (click to move)
</div>
<div id="ccbuffer"></div>
</div>
<script type="text/javascript">
$(document).ready(function() {
jwplayer.key = 'xxxxxxxxxxxxxxxxxxx';
jwplayer('myPlayer').setup({
width: '100%', aspectratio: '16:9', repeat: 'false', autostart: 'false',
playlist: [{
sources: [ { file: 'https:www.example.com/video.mp4'}],
tracks: [ { file: 'https:www.example.com/video-captions.vtt', kind: 'captions', label: 'English', 'default': true } ]
}]
})
// External CC Container
$('#ccContainer').hide();
var position = $('#myPlayer').position();
var width = $('#PlayerContainer').outerWidth();
ccTop = position.top;
ccLeft = (width+50)+'px'
$('#ccContainer').css({'top':ccTop, left:ccLeft });
var observer;
jwplayer().on('captionsList', function (event) {
ccObserver(event);
});
jwplayer().on('captionsChanged', function (event) {
ccObserver(event);
});
videoplayer.on('fullscreen', function(event){
if(event.fullscreen){
$('.jw-captions').css('display','block');
}else{
$('.jw-captions').css('display','none');
}
});
$("#ccFontFamily").change(function() {
$('#ccbuffer').css("font-family", $(this).val());
});
$("#ccFontSize").change(function() {
$('#ccbuffer').css("font-size", $(this).val() + "px");
});
$("#ccContrast").change(function() {
$('#ccContainer').removeClass("night highcontrast highcontrast2 highcontrast3").addClass( $(this).val() );
$('#ccContainerheader').removeClass("night highcontrast highcontrast2 highcontrast3").addClass( $(this).val() );
$('#ccbuffer').removeClass("night highcontrast highcontrast2 highcontrast3").addClass( $(this).val() );
$('select').removeClass("night highcontrast highcontrast2 highcontrast3").addClass( $(this).val() );
$('#ccFontReset').removeClass("night highcontrast highcontrast2 highcontrast3").addClass( $(this).val() );
});
$('#ccFontReset').click(function() {
ccFontReset();
});
function ccFontReset(){
$("#ccFontFamily").val($("#ccFontFamily option:first").val()).trigger('change');
$("#ccFontSize").val($("#ccFontSize option:first").val()).trigger('change');
$("#ccContrast").val($("#ccContrast option:first").val()).trigger('change');
}
ccFontReset();
});
function ccObserver(event){
if (event.track == 0) {
$('#ccContainer').hide('slow');
$('.jw-captions').css('display','block'); // VERY important
if (observer != null){
observer.disconnect();
}
}
else {
$('#ccContainer').show('slow');
$('.jw-captions').css('display','none'); // VERY important
var target = document.querySelector('.jw-captions');
observer = new MutationObserver(function(mutations) {
$('.jw-text-track-cue').each(function(i) {
if (i == 0)
$('#ccbuffer').html( $(this).text() );
else
$('#ccbuffer').append("<br/>" + $(this).text() );
});
});
var config = { attributes: true, childList: true, characterData: true }
observer.observe(target, config);
}
}
// External CC Container - Make the DIV element draggable:
dragElement(document.getElementById("ccContainer"));
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
</script>
</body>
</html>

Combining places search and kml layers with toggle on google maps api

I am attempting to create a map with multiple kml layers that can be toggled with the google places search, but have been unsuccessful. The purpose of this is to have the ability to search an address or intersection and determine the correct response apparatus for fire or medical calls for a dispatch center, with the search being biased toward the correct county. I have the base map with just one layer working here, but I've been unable to correctly integrate the kml layer toggle with the google places search. I had received some good input from geocodezip on using the geocode function here, but I wasn't able to bias it towards the correct location or include the predictive search completion. I also like the way that my original map will zoom and center on the searched address and would like to maintain that functionality. Admittedly, I am very much lacking in ability with this, and everything I have done has been to simply copy and modify working examples that others have posted, but I have not found any working examples that include both the google places search and kml layers with toggle. This is what I have so far. Any help in getting this to work would be very much appreciated.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<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;
}
</style>
<title>Advanced Backup Map</title>
<style>
#target {
width: 345px;
}
</style>
<script>
var kml = {
a: {
name: "Fire Response Areas",
url: "https://drive.google.com/uc?export=download&id=0B2gbIV1dXlvDVDhVLXc2N1Y5ZEE"
},
b: {
name: "SD Counties",
url: "https://drive.google.com/uc?export=download&id=0B2gbIV1dXlvDRndwdEpKTjRBeTA"
},
c: {
name: "Counties in Surrounding States",
url: "https://drive.google.com/uc?export=download&id=0B2gbIV1dXlvDRlhLTm93S2Y3eDQ"
}
// keep adding more if ye like
};
// This example adds a search box to a map, using the Google Place Autocomplete
// feature. People can enter geographical searches. The search box will return a
// pick list containing a mix of places and predicted search terms.
function initAutocomplete() {
// lets define some vars to make things easier later
var options = {
center: new google.maps.LatLng(43.64837, -96.73737),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById('map'), options);
createTogglers();
// Append Class on Select
function highlight(box, listitem) {
var selected = 'selected';
var normal = 'normal';
document.getElementById(listitem).className = (box.checked ? selected : normal);
};
function startup() {
// for example, this toggles kml a on load and updates the menu selector
var checkit = document.getElementById('a');
checkit.checked = true;
toggleKML(checkit, 'a');
highlight(checkit, 'selector1');
};
// Create the search box and link it to the UI element.
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(43.38218, -97.29373),
new google.maps.LatLng(43.92451, -96.34532));
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input, {
bounds: defaultBounds
});
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function() {
searchBox.setBounds(map.getBounds());
});
var markers = [];
// [START region_getplaces]
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
searchBox.addListener('places_changed', function() {
var places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach(function(marker) {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
var bounds = new google.maps.LatLngBounds();
places.forEach(function(place) {
var 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(25, 25)
};
// Create a marker for each place.
markers.push(new google.maps.Marker({
map: map,
icon: icon,
title: place.name,
position: place.geometry.location
}));
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
map.setZoom(16);
map.setCenter(center);
});
// [END region_getplaces]
// the important function... kml[id].xxxxx refers back to the top
function toggleKML(checked, id) {
if (checked) {
var layer = new google.maps.KmlLayer(kml[id].url, {
preserveViewport: true,
suppressInfoWindows: false
});
// store kml as obj
kml[id].obj = layer;
kml[id].obj.setMap(map);
}
else {
kml[id].obj.setMap(null);
delete kml[id].obj;
}
};
// create the controls dynamically because it's easier, really
function createTogglers() {
var html = "<form><ul>";
for (var prop in kml) {
html += "<li id=\"selector-" + prop + "\"><input type='checkbox' id='" + prop + "'" +
" onclick='highlight(this,\"selector-" + prop + "\"); toggleKML(this.checked, this.id)' \/>" +
kml[prop].name + "<\/li>";
}
document.getElementById("toggle_box").innerHTML = html;
};
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBhTQXonppCE2eVDQO5AHy11kMn-o27m_U&libraries=places&callback=initAutocomplete"
async defer></script>
</head>
<body>
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div id="map"></div>
<div id="toggle_box" style="position: absolute; top: 10px; right: 10px; padding: 5px; background: #fff; z-index: 1; "></div>
</body>
</html>

Shiny Server custom Handlebars.js templates

I'm not at all familiar with handlebars.js but I'd like to customize the directory index template that comes with Shiny Server. Specifically, what I'm looking to do is render a page of thumbnails of the different apps.
The file /opt/shiny-server/templates/directorIndex.html comes with the code below which reference a number of expressions including {{title}}, references to apps, dirs and files.
<!DOCTYPE html>
<html lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{title}}</title>
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro' rel='stylesheet' type='text/css'>
<style type="text/css">
body {
font-family: Helvetica, Arial, sans-serif;
background-color: #F5F5F5;
}
pre, tt, code, .code, #detail {
font-family: 'Consolas', 'Courier New', 'Courier', monospace;
}
h1 {
font-size: 40px;
}
a {
text-decoration: none;
}
</style>
</head>
<body>
<h1>{{title}}</h1>
<ul>
{{#each apps}}
<li><a class="code" href="{{this.url}}">{{this.name}}</a> (application)</li>
{{/each}}
{{#each dirs}}
<li><a class="code" href="{{this.url}}/">{{this.name}}</a></li>
{{/each}}
{{#each files}}
<li><a class="code" href="{{this.url}}">{{this.name}}</a></li>
{{/each}}
</ul>
</body>
</html>
So I have two questions.
First - how can I know what expressions are available to call?
Second - give that I just have this one html page (as far as I can tell) how do I register a helper, e.g.
Handlebars.registerHelper('splitURL', function(url) {
var t = url.split("/");
return t[1];
});
I had the same desire to customize the directoryIndex.html template and enjoyed the same lack of documentation about what handlebars expressions could be used. I'm not a web developer so the code here is probably rubbish, but it works well enough. It sounds like you already solved your issue, but others may find some use in this approach. Images for each app are saved in site_dir/images.
screenshot of end result
directoryIndex.html:
<!DOCTYPE html>
<html lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{title}}</title>
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro' rel='stylesheet' type='text/css'>
<link href="main.css" rel="stylesheet">
<script type='text/javascript'>
function include(arr,obj) {
return(arr.indexOf(obj) != -1);
}
function updateView(data) {
//update title and heading
if ("title" in data) {
var host = document.location.hostname;
if (host in data.title) {
document.title = data.title[host];
document.getElementById("title").innerHTML = data.title[host];
} else if ("default" in data.title) {
document.title = data.title.default;
document.getElementById("title").innerHTML = data.title.default;
}
}
//hide cards (for directories like /images)
if ("ignore" in data) {
var element;
for (var i in data.ignore) {
if (element = document.getElementById("card_"+data.ignore[i])) {
element.parentNode.removeChild(element);
}
}
}
//update each shiny app if it has JSON data
if ("apps" in data) {
for (var item in data.apps) {
if (document.getElementById("card_"+item)) {
if ("img" in data.apps[item])
document.getElementById("img_"+item).src = "/images/" + data.apps[item].img;
if ("name" in data.apps[item])
document.getElementById("name_"+item).innerHTML = data.apps[item].name;
if ("desc" in data.apps[item])
document.getElementById("desc_"+item).innerHTML = data.apps[item].desc;
}
}
}
}
function loadJSON(url) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(this.responseText);
updateView(data)
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
document.addEventListener("DOMContentLoaded", function() {
loadJSON("data.json");
});
</script>
</head>
<body>
<div id="title_bar">
<h1 id="title"></h1>
</div>
<div id="apps">
{{#each dirs}}
<div id="card_{{name}}" class="card" onclick="location.href='{{url}}';" style="cursor: pointer;">
<img id="img_{{name}}" src="" alt="{{name}}" onerror="if (this.src != '/images/missing.png') this.src = '/images/missing.png';">
<div class="container">
<h4 id="name_{{name}}">{{name}}</h4>
<p id="desc_{{name}}"></p>
</div>
</div>
{{/each}}
</div>
</body>
</html>
data.json (located in the site_dir root location):
{
"title": {
"default": "Shiny Server",
"dev_host": "Shiny Server (Development)",
"accp_host": "Shiny Server (Acceptance)",
"prod_host": "Shiny Server",
"dev_host.fully.qualified.name": "Shiny Server (Development)",
"accp_host.fully.qualified.name": "Shiny Server (Acceptance)",
"prod_host.fully.qualified.name": "Shiny Server"
},
"ignore": [ "app_4", "app_5", "images" ],
"apps": {
"app_1": {
"name": "app 1 name goes here",
"desc": "app 1 description goes here",
"img": "app1.png"
},
"app_2": {
"name": "app 2 name",
"desc": "app 2 desc",
"img": "app2.png"
},
"app_3": {
"name": "app 3 name",
"desc": "",
"img": "app3.png"
}
}
}
main.css (located in the site_dir root location):
body, html {
font-family: Helvetica, Arial, sans-serif;
background-color: #F5F5F5;
color: #114;
margin: 0;
padding: 0;
}
#title_bar {
height: 80px;
background-color: #3475b4;
overflow: hidden;
border-bottom: 1px solid #3475b3;
-moz-box-shadow: 0px 0px 10px 3px #BBC;
-webkit-box-shadow: 0px 0px 10px 3px #BBC;
box-shadow: 0px 0px 10px 3px #BBC;
}
#title_bar h1 {
margin: 14px auto .5em auto;
padding: .2em;
color: #EEE;
text-align: center;
}
#apps {
margin-top: 14px;
}
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
border-radius: 5px;
width: 300px;
margin: 10px;
display: inline-block;
vertical-align: top;
}
.card:hover {
box-shadow: 0 12px 24px 0 rgba(0,0,0,0.2);
}
.card img {
display: block;
margin: 0 auto;
max-width: 300px;
max-height: 250px;
}
.container {
padding: 2px 16px;
}

Google Maps API Key for 127.0.0.1

I've been working with Google Maps for a while in my local development environment (I'm working on http://127.0.0.1:5000), but lately I'm getting this error:
Google has disabled use of the Maps API for this application. See the Terms of Service for more information: http://www.google.com/intl/en-US_US/help/terms_maps.html.
In my Google API Console I have created an Browser Key, and I've tried the following domains:
http://127.0.0.1/*
http://localhost/*
http://127.0.0.1:5000/*
http://0.0.0.0:5000/*
http://*:*/*
Obviously, not being about to see the map is making local development quite tricky. But I cannot figure out how to get around this. Can anyone point me at the correct configuration/approach to resolve this?
The documentation doesn't seem to talk about this sort of thing, which isn't helpful.
I got the same trouble, and the issue is to remove the key param in the adress as suggested by Mano Marks.
Just change
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&signed_in=true&callback=initMap"
async defer></script>
to
<script src="https://maps.googleapis.com/maps/api/js?signed_in=true&callback=initMap"
async defer></script>
Below is a full working code (HTML + JS)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Reverse Geocoding</title>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
#floating-panel {
position: absolute;
top: 10px;
left: 25%;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
text-align: center;
font-family: 'Roboto','sans-serif';
line-height: 30px;
padding-left: 10px;
}
</style>
<style>
#floating-panel {
position: absolute;
top: 5px;
left: 50%;
margin-left: -180px;
width: 350px;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
}
#latlng {
width: 225px;
}
</style>
</head>
<body>
<div id="floating-panel">
<input id="latlng" type="text" value="40.714224,-73.961452">
<input id="submit" type="button" value="Reverse Geocode">
</div>
<div id="map"></div>
<script>
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 8,
center: {lat: 40.731, lng: -73.997}
});
var geocoder = new google.maps.Geocoder;
var infowindow = new google.maps.InfoWindow;
document.getElementById('submit').addEventListener('click', function() {
geocodeLatLng(geocoder, map, infowindow);
});
}
function geocodeLatLng(geocoder, map, infowindow) {
var input = document.getElementById('latlng').value;
var latlngStr = input.split(',', 2);
var latlng = {lat: parseFloat(latlngStr[0]), lng: parseFloat(latlngStr[1])};
geocoder.geocode({'location': latlng}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
if (results[1]) {
map.setZoom(11);
var marker = new google.maps.Marker({
position: latlng,
map: map
});
infowindow.setContent(results[1].formatted_address);
infowindow.open(map, marker);
} else {
window.alert('No results found');
}
} else {
window.alert('Geocoder failed due to: ' + status);
}
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?signed_in=true&callback=initMap"
async defer></script>
</body>
</html>

Google Places API mapping a company

I'm trying to plot a marker in a Google Map where a company is, so for example I am looking for the Apple Store in Aberdeen, Scotland I would like a marker there and when I click the marker it will show the info window with the company details which is generated from Google with reviews etc.
I can't find any documentation on how to do this, but the closest I found is a Company Lookup which then points you to the location. I'd rather be able to cut out the start and just have the company name already in the code and pointing the map straight to the location.
Could anyone possibly help at all? Thanks
<!DOCTYPE html>
<html>
<head>
<title>Place Autocomplete</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
html, body, #map-canvas {
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;
}
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
<script>
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(-33.8688, 151.2195),
zoom: 13
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
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
});
google.maps.event.addListener(autocomplete, 'place_changed', function() {
infowindow.close();
marker.setVisible(false);
var place = autocomplete.getPlace();
if (!place.geometry) {
return;
}
// If the place has a geometry, then present it on a map.
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(17); // Why 17? Because it looks good.
}
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);
});
// Sets a listener on a radio button to change the filter type on Places
// Autocomplete.
function setupClickListener(id, types) {
var radioButton = document.getElementById(id);
google.maps.event.addDomListener(radioButton, 'click', function() {
autocomplete.setTypes(types);
});
}
setupClickListener('changetype-all', []);
setupClickListener('changetype-establishment', ['establishment']);
setupClickListener('changetype-geocode', ['geocode']);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<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-geocode">
<label for="changetype-geocode">Geocodes</label>
</div>
<div id="map-canvas"></div>
</body>
</html>
The "google" infowindow isn't available from the Places API, you need to build it yourself from the information available in the places details response or link to a google map.

Resources