I've been messing around with tutorial on midi and triggering samples. However I can't figure out how to remove the random pitch change of the samples in the code. Any idea how to remove the pitch change?
link to tutorial:http://www.keithmcmillen.com/blog/making-music-in-the-browser-web-midi-api/
<html>
<head>
<meta charset="UTF-8">
<title>Web MIDI API</title>
<style>
*:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
h4 {
margin: 0 0 5px 0;
}
p {
margin: 0 0 10px 0;
}
#content, #device_info {
max-width: 800px;
margin: 0 auto;
padding: 10px 0;
font-family: sans-serif;
font-size: 12px;
line-height: 12px;
letter-spacing: 1.5px;
}
#content, #key_data {
margin-top: 0px;
text-align: center;
}
#inputs, #outputs {
display: inline-block;
width: 49%;
margin-top: 10px;
vertical-align: top;
}
#outputs {
text-align: right;
}
.info {
padding: 20px;
border-radius: 3px;
box-shadow: inset 0 0 10px #ccc;
background-color: rgba(233,233,233,0.25);
}
.small {
border-bottom: 1px solid #ccc;
margin-left: 10px;
}
p:not(.small){
text-transform: uppercase;
font-weight: 800;
}
.button {
display: inline-block;
width: 100px;
height: 100px;
margin: 10px;
background-color: #00adef;
border-radius: 10px;
opacity: 1;
cursor: pointer;
border: 2px solid white;
transition: all 0.2s;
}
.button.active {
background-color: #9a6aad;
opacity: 0.25;
box-shadow: inset 0px 0px 30px orange;
border: 2px solid rgba(100,100,100,0.3);
animation: shake .2s ease-in-out;
}
#keyframes shake {
0% {
transform: translateX(0);
transform: translateY(0);
transform: scale(1,1);
}
20% {
transform: translateX(-10px);
transform: translateY(-100px);
transform: scale(0.5,0.75);
}
40% {
transform: translateX(10px);
transform: translateY(0px);
transform: scale(1.5,2);
}
60% {
transform: translateX(-10px);
transform: translateY(-50px);
transform: scale(0.5,0.75);
}
80% {
transform: translateX(10px);
transform: translateY(50px);
transform: scale(1.3,2);
}
100% {
transform: translateX(0);
transform: translateY(0);
}
}
</style>
</head>
<body>
<div id="content">
<div class="button" data-key="q" data-sound="audio/Slice1.mp3"></div>
<div class="button" data-key="w" data-sound="audio/dinky-snare.mp3"></div>
<div class="button" data-key="e" data-sound="audio/dinky-hat-2.mp3"></div>
<div class="button" data-key="r" data-sound="audio/dinky-cym.mp3"></div>
<div class="button" data-key="t" data-sound="audio/dinky-cym-noise.mp3"></div>
</div>
<div id="device_info">
<div id="key_data"></div>
<div id="inputs"></div>
<div id="outputs"></div>
</div>
<script>
//http://stackoverflow.com/questions/23687635/how-to-stop-audio-in-an-iframe-using-web-audio-api-after-hiding-its-container-di
(function(){
var log = console.log.bind(console), keyData = document.getElementById('key_data'),
deviceInfoInputs = document.getElementById('inputs'), deviceInfoOutputs = document.getElementById('outputs'), midi;
var AudioContext = AudioContext || webkitAudioContext; // for ios/safari
var context = new AudioContext();
var activeNotes = [];
var btnBox = document.getElementById('content'), btn = document.getElementsByClassName('button');
var data, cmd, channel, type, note, velocity;
// request MIDI access
if(navigator.requestMIDIAccess){
navigator.requestMIDIAccess({sysex: false}).then(onMIDISuccess, onMIDIFailure);
}
else {
alert("No MIDI support in your browser.");
}
// add event listeners
document.addEventListener('keydown', keyController);
document.addEventListener('keyup', keyController);
for(var i = 0; i < btn.length; i++){
btn[i].addEventListener('mousedown', clickPlayOn);
btn[i].addEventListener('mouseup', clickPlayOff);
}
// prepare audio files
for(var i = 0; i < btn.length; i++){
addAudioProperties(btn[i]);
}
var sampleMap = {
key60: 1,
key61: 2,
key62: 3,
key63: 4,
key64: 5
};
// user interaction
function clickPlayOn(e){
e.target.classList.add('active');
e.target.play();
}
function clickPlayOff(e){
e.target.classList.remove('active');
}
function keyController(e){
if(e.type == "keydown"){
switch(e.keyCode){
case 81:
btn[0].classList.add('active');
btn[0].play();
break;
case 87:
btn[1].classList.add('active');
btn[1].play();
break;
case 69:
btn[2].classList.add('active');
btn[2].play();
break;
case 82:
btn[3].classList.add('active');
btn[3].play();
break;
case 84:
btn[4].classList.add('active');
btn[4].play();
break;
default:
//console.log(e);
}
}
else if(e.type == "keyup"){
switch(e.keyCode){
case 81:
btn[0].classList.remove('active');
break;
case 87:
btn[1].classList.remove('active');
break;
case 69:
btn[2].classList.remove('active');
break;
case 82:
btn[3].classList.remove('active');
break;
case 84:
btn[4].classList.remove('active');
break;
default:
//console.log(e.keyCode);
}
}
}
// midi functions
function onMIDISuccess(midiAccess){
midi = midiAccess;
var inputs = midi.inputs.values();
// loop through all inputs
for(var input = inputs.next(); input && !input.done; input = inputs.next()){
// listen for midi messages
input.value.onmidimessage = onMIDIMessage;
listInputs(input);
}
// listen for connect/disconnect message
midi.onstatechange = onStateChange;
showMIDIPorts(midi);
}
function onMIDIMessage(event){
data = event.data,
cmd = data[0] >> 4,
channel = data[0] & 0xf,
type = data[0] & 0xf0, // channel agnostic message type. Thanks, Phil Burk.
note = data[1],
velocity = data[2];
// with pressure and tilt off
// note off: 128, cmd: 8
// note on: 144, cmd: 9
// pressure / tilt on
// pressure: 176, cmd 11:
// bend: 224, cmd: 14
log('MIDI data', data);
switch(type){
case 144: // noteOn message
noteOn(note, velocity);
break;
case 128: // noteOff message
noteOff(note, velocity);
break;
}
//log('data', data, 'cmd', cmd, 'channel', channel);
logger(keyData, 'key data', data);
}
function onStateChange(event){
showMIDIPorts(midi);
var port = event.port, state = port.state, name = port.name, type = port.type;
if(type == "input")
log("name", name, "port", port, "state", state);
}
function listInputs(inputs){
var input = inputs.value;
log("Input port : [ type:'" + input.type + "' id: '" + input.id +
"' manufacturer: '" + input.manufacturer + "' name: '" + input.name +
"' version: '" + input.version + "']");
}
function noteOn(midiNote, velocity){
player(midiNote, velocity);
}
function noteOff(midiNote, velocity){
player(midiNote, velocity);
}
function player(note, velocity){
var sample = sampleMap['key'+note];
if(sample){
if(type == (0x80 & 0xf0) || velocity == 0){ //needs to be fixed for QuNexus, which always returns 144
btn[sample - 1].classList.remove('active');
return;
}
btn[sample - 1].classList.add('active');
btn[sample - 1].play(velocity);
}
}
function onMIDIFailure(e){
log("No access to MIDI devices or your browser doesn't support WebMIDI API. Please use WebMIDIAPIShim " + e);
}
// MIDI utility functions
function showMIDIPorts(midiAccess){
var inputs = midiAccess.inputs,
outputs = midiAccess.outputs,
html;
html = '<h4>MIDI Inputs:</h4><div class="info">';
inputs.forEach(function(port){
html += '<p>' + port.name + '<p>';
html += '<p class="small">connection: ' + port.connection + '</p>';
html += '<p class="small">state: ' + port.state + '</p>';
html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
if(port.version){
html += '<p class="small">version: ' + port.version + '</p>';
}
});
deviceInfoInputs.innerHTML = html + '</div>';
html = '<h4>MIDI Outputs:</h4><div class="info">';
outputs.forEach(function(port){
html += '<p>' + port.name + '<br>';
html += '<p class="small">manufacturer: ' + port.manufacturer + '</p>';
if(port.version){
html += '<p class="small">version: ' + port.version + '</p>';
}
});
deviceInfoOutputs.innerHTML = html + '</div>';
}
// audio functions
function loadAudio(object, url){
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(){
context.decodeAudioData(request.response, function(buffer){
object.buffer = buffer;
});
}
request.send();
}
function addAudioProperties(object){
object.name = object.id;
object.source = object.dataset.sound;
loadAudio(object, object.source);
object.play = function(volume){
var s = context.createBufferSource();
var g = context.createGain();
var v;
s.buffer = object.buffer;
s.playbackRate.value = randomRange(0.5, 2);
if(volume){
v = rangeMap(volume, 1, 127, 0.2, 2);
s.connect(g);
g.gain.value = v * v;
g.connect(context.destination);
}
else{
s.connect(context.destination);
}
s.start();
object.s = s;
}
}
// utility functions
function randomRange(min, max){
return Math.random() * (max + min) + min;
}
function rangeMap(x, a1, a2, b1, b2){
return ((x - a1)/(a2-a1)) * (b2 - b1) + b1;
}
//function frequencyFromNoteNumber( note ) {
// return 440 * Math.pow(2,(note-69)/12);
//}
function logger(container, label, data){
messages = label + " [channel: " + (data[0] & 0xf) + ", cmd: " + (data[0] >> 4) + ", type: " + (data[0] & 0xf0) + " , note: " + data[1] + " , velocity: " + data[2] + "]";
container.textContent = messages;
}
})();
</script>
</body>
</html>
There is a line in the addAudioProperties() function which sets the playback rate to random every time. You can remove this by setting the playback rate to 1:
s.playbackRate.value = randomRange(0.5, 2);
becomes
s.playbackRate.value = 1;
Related
Let me just say that my knowledge in coding is very minimal. I am trying to figure out how to turn the hamburger menu from the current color to white when the cursor hovers it or when you click it. I hope that makes sense! It will be three white lines on a black background, so it will be visible.
I have tried adding color: #fff; but I think there needs to be some specific line of coding to actually change the hamburger menu on hover/click. I am unaware of this code.
Please let me know if I included the right CSS for what I am asking about! I added the custom js in case it is needed!
/* Header */
.header-wrap #logo {
display: table-cell;
vertical-align: middle;
width: 100%;
font-size: 2em;
text-align: center;
}
.header-wrap #logo #wsite-title {
font-size: inherit !important;
}
.header-wrap .wsite-logo {
padding: 0 50px;
}
.header-wrap .wsite-logo a img {
max-height: 40px;
}
.header-wrap .search {
display: none;
}
.header-wrap .nav-wrap {
position: fixed;
display: table;
background: #shade;
top: 0;
left: 0;
z-index: 15;
height: 55px;
-webkit-transition: all 610ms cubic-bezier(0, 0.8, 0.55, 1);
-moz-transition: all 610ms cubic-bezier(0, 0.8, 0.55, 1);
-ms-transition: all 610ms cubic-bezier(0, 0.8, 0.55, 1);
-o-transition: all 610ms cubic-bezier(0, 0.8, 0.55, 1);
transition: all 610ms cubic-bezier(0, 0.8, 0.55, 1);
}
.header-wrap .nav-wrap ul {
padding: 0 4em;
}
.header-wrap .hamburger {
position: absolute;
top: 0;
right: 0;
display: block;
width: 30px;
height: 30px;
padding: 10px;
cursor: pointer;
}
.header-wrap .hamburger span,
.header-wrap .hamburger span:before,
.header-wrap .hamburger span:after {
position: relative;
display: block;
width: 20px;
height: 2px;
background: #color;
content: '';
}
.header-wrap .hamburger span {
top: 10px;
left: 4px;
margin: 6px 0;
}
.header-wrap .hamburger span:before {
top: -8px;
}
.header-wrap .hamburger span:after {
bottom: -6px;
}
jQuery(function($) {
// Mobile sidebars
$.fn.expandableSidebar = function(expandedClass) {
var $me = this;
$me.on('click', function() {
if(!$me.hasClass(expandedClass)) {
$me.addClass(expandedClass);
} else {
$me.removeClass(expandedClass);
}
});
}
var haberdasherController = {
init: function(opts) {
var base = this;
// Add classes to elements
base._attachEvents();
setTimeout(function(){
base._addClasses();
base._checkCartItems();
}, 1000);
},
_addClasses: function() {
var base = this;
// Add placeholder text to inputs
$('.wsite-form-sublabel').each(function(){
var sublabel = $(this).text();
$(this).prev('.wsite-form-input').attr('placeholder', sublabel);
});
// Add fullwidth class to gallery thumbs if less than 6
$('.imageGallery').each(function(){
if ($(this).children('div').length <= 6) {
$(this).children('div').addClass('fullwidth-mobile');
}
});
},
_stickyFooter: function() {
var stickyFooterMargin = $('#footer-wrap').height();
$('.wrapper').css('margin-bottom', -stickyFooterMargin);
$('#footer-wrap, .sticky-footer-push').css('height', stickyFooterMargin);
},
_checkCartItems: function() {
var base = this;
if($('#wsite-mini-cart').find('li.wsite-product-item').length > 0) {
$('#wsite-mini-cart').addClass('full');
base.cartHasItems = true;
} else {
$('#wsite-mini-cart').removeClass('full');
base.cartHasItems = false;
}
},
_attachEvents: function() {
var base = this;
$('.hamburger').on('click', function(e) {
e.preventDefault();
$('body').toggleClass('nav-open');
});
// Pad header on mobile
setTimeout(function(){
if($(window).width() < 992) {
$(".banner-wrap").css({"padding-top" : $(".header-wrap > .nav-wrap").height() + "px"});
}
}, 800);
// Copy login and search to mobile nav
var login = $("#member-login").clone(true),
search = $("#wsite-header-search-form").clone(true)
$("#navmobile .wsite-menu-default").append(login).append(search);
// Menu text alignment
if($('.search').is(':empty') || $('.search').css('display') == 'none') {
$('.menu').css('text-align', 'center');
}
// Store category dropdown
$('.wsite-com-sidebar').expandableSidebar('sidebar-expanded');
// Search filters dropdown
$('#wsite-search-sidebar').expandableSidebar('sidebar-expanded');
// Init fancybox swipe on mobile
if('ontouchstart' in window) {
$('body').on('click', 'a.w-fancybox', function() {
base._initSwipeGallery();
});
}
// Init sticky footer
if($(window).width() > 992) {
base._stickyFooter();
}
},
_initSwipeGallery: function() {
var base = this;
setTimeout(function(){
var touchGallery = document.getElementsByClassName('fancybox-wrap')[0];
var mc = new Hammer(touchGallery);
mc.on("panleft panright", function(ev) {
if (ev.type == "panleft") {
$("a.fancybox-next").trigger("click");
} else if (ev.type == "panright") {
$("a.fancybox-prev").trigger("click");
}
base._initSwipeGallery();
});
}, 500);
}
}
$(document).ready(function(){
haberdasherController.init();
});
});
I am new to d3js and I need some help for my project. I want to my map to change color on dropdown change. I have added two maps in my code, default would be data/world-map.json, and I want to change to data/2000.json when 2000 is selected from dropdown menu. Any help would be appreciated.
Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<title>Project</title>
<style>
.country {
stroke: white;
stroke-width: 0.5px;
}
.country:hover{
stroke: #fff;
stroke-width: 1.5px;
}
.text{
font-size:10px;
text-transform:capitalize;
}
#container {
margin:10px 10%;
border:2px solid #000;
border-radius: 5px;
height:100%;
overflow:hidden;
background: #F0F8FF;
}
.hidden {
display: none;
}
div.tooltip {
color: #222;
background: #fff;
padding: .5em;
text-shadow: #f5f5f5 0 1px 0;
border-radius: 5px;
box-shadow: 0px 0px 2px 0px #a6a6a6;
opacity: 0.9;
position: absolute;
}
.graticule {
fill: none;
stroke: #bbb;
stroke-width: .5px;
stroke-opacity: .5;
}
.equator {
stroke: #ccc;
stroke-width: 1px;
}
</style>
</head>
<body>
<h1 align="center">FIFA PLAYER OF THE YEAR 2000 - 2010</h1>
<select id="select_button" name="select_button">
<option value="0" selected="selected">Select year</option>
<option value="1">2000.</option>
<option value="2">2001.</option>
<option value="3">2002.</option>
<option value="4">2003.</option>
<option value="5">2004.</option>
<option value="6">2005.</option>
<option value="7">2006.</option>
<option value="8">2007.</option>
<option value="9">2008.</option>
<option value="10">2009.</option>
<option value="11">2010.</option>
</select>
<div id="container"></div>
<script src="js/d3.min.js"></script>
<script src="js/topojson.v1.min.js"></script>
<script>
d3.select(window).on("resize", throttle);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 9])
.on("zoom", move);
var width = document.getElementById('container').offsetWidth;
var height = width / 2;
var topo,projection,path,svg,g;
var currentKey = '0';
d3.select('#select_button').on('change', function(a) {
currentKey = d3.select(this).property('value');
draw(topo);
});
var graticule = d3.geo.graticule();
var tooltip = d3.select("#container").append("div").attr("class", "tooltip hidden");
setup(width,height);
function setup(width,height){
projection = d3.geo.mercator()
.translate([(width/2), (height/2)])
.scale( width / 2 / Math.PI);
path = d3.geo.path().projection(projection);
svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom)
.on("click", click)
.append("g");
g = svg.append("g");
}
d3.json("data/world-map.json", function(error, world) {
var countries = topojson.feature(world, world.objects.countries).features;
topo = countries;
draw(topo);
});
d3.json("data/2000.json", function(error, world) {
var countries2 = topojson.feature(world, world.objects.countries).features;
topo = countries2;
draw(topo);
});
d3.selectAll("path").remove();
function draw(topo) {
/* svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
g.append("path")
.datum({type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]]})
.attr("class", "equator")
.attr("d", path);
*/
var country = g.selectAll(".country").data(topo);
country.enter().insert("path")
.attr("class", "country")
.attr("d", path)
.attr("id", function(d,i) { return d.id; })
.attr("title", function(d,i) { return d.properties.name; })
.style("fill", function(d, i) { return d.properties.color; });
//offsets for tooltips
var offsetL = document.getElementById('container').offsetLeft+20;
var offsetT = document.getElementById('container').offsetTop+10;
//tooltips
country
.on("mousemove", function(d,i) {
var mouse = d3.mouse(svg.node()).map( function(d) { return parseInt(d); } );
tooltip.classed("hidden", false)
.attr("style", "left:"+(mouse[0]+offsetL)+"px;top:"+(mouse[1]+offsetT)+"px")
.html(d.properties.name);
})
.on("mouseout", function(d,i) {
tooltip.classed("hidden", true);
});
/*
//EXAMPLE: adding some capitals from external CSV file
d3.csv("data/country-capitals.csv", function(err, capitals) {
capitals.forEach(function(i){
addpoint(i.CapitalLongitude, i.CapitalLatitude, i.CapitalName );
});
}); */
}
function redraw() {
width = document.getElementById('container').offsetWidth;
height = width / 2;
d3.select('svg').remove();
setup(width,height);
draw(topo);
}
function move() {
var t = d3.event.translate;
var s = d3.event.scale;
zscale = s;
var h = height/4;
t[0] = Math.min(
(width/height) * (s - 1),
Math.max( width * (1 - s), t[0] )
);
t[1] = Math.min(
h * (s - 1) + h * s,
Math.max(height * (1 - s) - h * s, t[1])
);
zoom.translate(t);
g.attr("transform", "translate(" + t + ")scale(" + s + ")");
//adjust the country hover stroke width based on zoom level
d3.selectAll(".country").style("stroke-width", 0.5 / s);
}
var throttleTimer;
function throttle() {
window.clearTimeout(throttleTimer);
throttleTimer = window.setTimeout(function() {
redraw();
}, 200);
}
//geo translation on mouse click in map
function click() {
var latlon = projection.invert(d3.mouse(this));
console.log(latlon);
}
/*
//function to add points and text to the map (used in plotting capitals)
function addpoint(lat,lon,text) {
var gpoint = g.append("g").attr("class", "gpoint");
var x = projection([lat,lon])[0];
var y = projection([lat,lon])[1];
gpoint.append("svg:circle")
.attr("cx", x)
.attr("cy", y)
.attr("class","point")
.attr("r", 1.5);
//conditional in case a point has no associated text
if(text.length>0){
gpoint.append("text")
.attr("x", x+2)
.attr("y", y+2)
.attr("class","text")
.text(text);
}
}*/
</script>
</body>
</html>
I'm hoping to combine the following two codes to create a map that not only searches with a layer of KMZ on top, but also shows navigation options in the infowindow. An example of the two maps side by side can be viewed here. But I want to combine the two maps into one. I've included my attempt at this at the bottom of this post - but it doesn't work.
Firstly, I've adapted this code for searching a map with my kmz (or kml) layered onto it and it works just fine ...
<!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-canvas {
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>Places Searchbox</title>
<style>
#target {
width: 345px;
}
</style>
</head>
<body>
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div id="map-canvas"></div>
<script>
// 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() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
center: {lat:53.6292604,lng:-2.9380916},
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var mapLayer = new google.maps.KmlLayer({
url: "https://www.google.com/maps/d/kml?mid=zQWA66D2AmlU.kGvUUZ4wvdYo",
suppressInfoWindows:false,
preserveViewport:true });
mapLayer.setMap(map);
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
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] this was the first to cause problems
// 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);
});
// [END region_getplaces]
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=[MY-KEY]&libraries=places&callback=initAutocomplete"
async defer></script>
</body>
</html>
This second code works great with adding a direction link to the info window so you can drive there using your smartphone ...
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8"><title>Embedded Map</title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3"></script>
<script>
var map;
var infowindow;
function initialize() {
var mapOptions = {center: {lat:53.6292604,lng:-2.9380916},
zoom: 5}
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var mapLayer = new google.maps.KmlLayer({
url: "https://www.google.com/maps/d/kml?mid=zQWA66D2AmlU.kGvUUZ4wvdYo",
suppressInfoWindows:true,
preserveViewport:true });
mapLayer.setMap(map);
google.maps.event.addListener(mapLayer, 'click', function(kmlEvent) {
var text = kmlEvent.featureData.infoWindowHtml;
text = text + '<br>Navigate to Here';
if (infowindow) { infowindow.setContent(text);
} else {
infowindow = new google.maps.InfoWindow({content: text});
}
infowindow.setOptions({position:kmlEvent.latLng, pixelOffset:kmlEvent.pixelOffset});
infowindow.open(map);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script></head>
<body>
<div id="map-canvas"></div>
</body></html>
Putting the two scripts together here, it looks something like this, although this doesn't work. Any answers gratefully received about whats going wrong here please ...
<!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-canvas {
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>Places Searchbox</title>
<style>
#target {
width: 345px;
}
</style>
</head>
<body>
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div id="map-canvas"></div>
<script>
// 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() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
center: {lat:53.6292604,lng:-2.9380916},
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var mapLayer = new google.maps.KmlLayer({
url: "https://www.google.com/maps/d/kml?mid=zQWA66D2AmlU.kGvUUZ4wvdYo",
suppressInfoWindows:false,
preserveViewport:true });
mapLayer.setMap(map);
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
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] this was the first to cause problems
// 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);
});
// [END region_getplaces]
var map;
var infowindow;
mapLayer.setMap(map);
google.maps.event.addListener(mapLayer, 'click', function(kmlEvent) {
var text = kmlEvent.featureData.infoWindowHtml;
text = text + '<br>Navigate to Here';
if (infowindow) { infowindow.setContent(text);
} else {
infowindow = new google.maps.InfoWindow({content: text});
}
infowindow.setOptions({position:kmlEvent.latLng, pixelOffset:kmlEvent.pixelOffset});
infowindow.open(map);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=[MY-KEY]&libraries=places&callback=initAutocomplete"
async defer></script>
</body>
</html>
remove the final } in the javascript, it generates a syntax error: Uncaught SyntaxError: Unexpected token }.
change suppressInfoWindows: false to suppressInfoWindows: true (removes the extraneous second copy of the infowindow).
code snippet:
// 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() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
center: {
lat: 53.6292604,
lng: -2.9380916
},
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var mapLayer = new google.maps.KmlLayer({
url: "https://www.google.com/maps/d/kml?mid=zQWA66D2AmlU.kGvUUZ4wvdYo",
suppressInfoWindows: true,
preserveViewport: true
});
mapLayer.setMap(map);
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
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] this was the first to cause problems
// 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);
});
// [END region_getplaces]
var map;
var infowindow;
mapLayer.setMap(map);
google.maps.event.addListener(mapLayer, 'click', function(kmlEvent) {
var text = kmlEvent.featureData.infoWindowHtml;
text = text + '<br>Navigate to Here';
if (infowindow && infowindow.setContent) {
infowindow.setContent(text);
} else {
infowindow = new google.maps.InfoWindow({
content: text
});
}
infowindow.setOptions({
position: kmlEvent.latLng,
pixelOffset: kmlEvent.pixelOffset
});
infowindow.open(map);
});
}
google.maps.event.addDomListener(window, 'load', initAutocomplete);
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#map-canvas {
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>Places Searchbox</title> <style> #target {
width: 345px;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=places"></script>
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div id="map-canvas"></div>
I'm trying to get a d3 globe to rotate to a particular country when you click that country in a list. To start out, I'm trying to get the following example working (I got it from http://bl.ocks.org/KoGor/5994804), but it throws an error TypeError: world is undefined from line 100. Can anyone help, please?:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Earth globe</title>
<script src="./d3/d3.v3.min.js"></script>
<script src="./d3/topojson.v1.min.js"></script>
<script src="./d3/queue.v1.min.js"></script>
</head>
<style type="text/css">
.water {
fill: #00248F;
}
.land {
fill: #A98B6F;
stroke: #FFF;
stroke-width: 0.7px;
}
.land:hover {
fill:#33CC33;
stroke-width: 1px;
}
.focused {
fill: #33CC33;
}
select {
position: absolute;
top: 20px;
left: 580px;
border: solid #ccc 1px;
padding: 3px;
box-shadow: inset 1px 1px 2px #ddd8dc;
}
.countryTooltip {
position: absolute;
display: none;
pointer-events: none;
background: #fff;
padding: 5px;
text-align: left;
border: solid #ccc 1px;
color: #666;
font-size: 14px;
font-family: sans-serif;
}
</style>
<body>
<script>
var width = 600,
height = 500,
sens = 0.25,
focused;
//Setting projection
var projection = d3.geo.orthographic()
.scale(245)
.rotate([0, 0])
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path()
.projection(projection);
//SVG container
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Adding water
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "water")
.attr("d", path);
var countryTooltip = d3.select("body").append("div").attr("class", "countryTooltip"),
countryList = d3.select("body").append("select").attr("name", "countries");
queue()
.defer(d3.json, "http://bl.ocks.org/KoGor/raw/5685937/world-110m.json")
.defer(d3.tsv, "http://bl.ocks.org/KoGor/raw/5685937/world-110m-country-names.tsv")
.await(ready);
//Main function
function ready(error, world, countryData) {
var countryById = {},
countries = topojson.feature(world, world.objects.countries).features;
//Adding countries to select
countryData.forEach(function(d) {
countryById[d.id] = d.name;
option = countryList.append("option");
option.text(d.name);
option.property("value", d.id);
});
//Drawing countries on the globe
var world = svg.selectAll("path.land")
.data(countries)
.enter().append("path")
.attr("class", "land")
.attr("d", path)
//Drag event
.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: r[0] / sens, y: -r[1] / sens}; })
.on("drag", function() {
var rotate = projection.rotate();
projection.rotate([d3.event.x * sens, -d3.event.y * sens, rotate[2]]);
svg.selectAll("path.land").attr("d", path);
svg.selectAll(".focused").classed("focused", focused = false);
}))
//Mouse events
.on("mouseover", function(d) {
countryTooltip.text(countryById[d.id])
.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px")
.style("display", "block")
.style("opacity", 1);
})
.on("mouseout", function(d) {
countryTooltip.style("opacity", 0)
.style("display", "none");
})
.on("mousemove", function(d) {
countryTooltip.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px");
});
//Country focus on option select
d3.select("select").on("change", function() {
var rotate = projection.rotate(),
focusedCountry = country(countries, this),
p = d3.geo.centroid(focusedCountry);
svg.selectAll(".focused").classed("focused", focused = false);
//Globe rotating
(function transition() {
d3.transition()
.duration(2500)
.tween("rotate", function() {
var r = d3.interpolate(projection.rotate(), [-p[0], -p[1]]);
return function(t) {
projection.rotate(r(t));
svg.selectAll("path").attr("d", path)
.classed("focused", function(d, i) { return d.id == focusedCountry.id ? focused = d : false; });
};
})
})();
});
function country(cnt, sel) {
for(var i = 0, l = cnt.length; i < l; i++) {
if(cnt[i].id == sel.value) {return cnt[i];}
}
};
};
</script>
</body>
</html>
My page has a scatter plot of data values for a list of genes. The list can be 30,000+. I have all the data on the client, so my autocomplete is as follows:
html:
<label>Search for a specific gene:</label> <input size=25 maxlength=100 id=gene_autocomplete class="autocomplete" />
css:
.ui-autocomplete {
max-width: 275px;
max-height: 300px;
overflow-y: auto;
overflow-x: hidden;
padding-right: 20px;
}
ul.ui-autocomplete li.ui-menu-item {
font-size: 11px;
margin-right: 15px;
}
Javascript:
$(document).ready(function () {
...
$("input#gene_autocomplete").autocomplete({source: data.ids, minLength:4});
... followed by parsing a data file
parseGct: function(dataText) {
var rows = dataText.split('\n');
this.vals = new Array();
this.colNames = rows[2].split('\t').splice(2);
for(var i = 3; i < rows.length; i++) {
var cols = rows[i].split('\t');
if(cols.length < 2) continue;
var data = cols.slice(2);
this.toFloat(data);
this.vals.push(data.slice());
}
},
I have experimented with different delays (0 to 700), to no avail. Generally the browser times out. What can I do to speed this up?