Hi I have map on which I can show different layers. By clicking on each feature contained in the layer(s) a popup window appear with information about that feature. Everything works fine, but if I am in FullScreen mode the popup window is hidden (it is called but hidden by the fullscreen mode), although a maximum z-index was given to it.
The map is online at the following link
https://www.marinemammalhabitat.org/imma-eatlas/
Anyone can help me in solving the thing? Thanks
The following is the code for the popup window:
/code to show popups containing layer's features
var info = document.getElementById('info');
var target = document.getElementById('map');
function displayFeatureInfo(pixel) {
info.style.left = '30%';
info.style.top = '20%';
info.style.height = 300 + 'px';
var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
return feature;
});
if (feature) {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
var text = '<table><tbody>';
//if (feature.get('AOI')) {text += '<tr><td> <h2> ' + feature.get('AOI') + '</h2></td></tr>';} else {text += '';}
text += '<tr><td style="background-color: rgba(140, 177, 222, 0.5);"> <h2> ' + feature.get('Title') + '</h2></td></tr>';
text += '<tr><td><strong>Summary: </strong>' + feature.get('Summary') + '</h2></td></tr>';
text += '<tr><td style="background-color: rgba(140, 177, 222, 0.5);"><strong>Region:</strong> ' + feature.get('Region') + '</td></tr>';
if (feature.get('AOI')) {text += '';} else {
text += '<tr><td> <strong>Criteria:</strong> ' + feature.get('Criteria') + '</td></tr>';
}
if (feature.get('AOI')) {text += '';} else {
text += '<tr><td style="background-color: rgba(140, 177, 222, 0.5);"> <strong>Species:</strong> ' + feature.get('Species') + '</td></tr>';
}
if (feature.get('Status')) {text += '<tr><td> <strong>Status:</strong> ' + feature.get('Status') + '</td></tr>';} else {text += '';}
if (feature.get('URL')) {text += '<tr><td> MORE INFO </td></tr>';} else {text += '<tr><td> </td></tr>';}
//text += '<tr><td> MORE INFO </td></tr>';
text += '</tbody></table>';
info.style.display = 'none';
info.innerHTML = text;
info.style.display = 'block';
target.style.cursor = "pointer";
} else {
info.style.display = 'none';
target.style.cursor = "";
}
}
map.on('click', function(evt) {
if (evt.dragging) {
info.style.display = 'none';
return;
}
var pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
//ends code for popups
CSS for the #info div is as follows:
#info {
position: absolute;
z-index: 2147483647;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 8px;
color: #000000;
padding: 10px;
font-size: 14px;
font-weight:500;
top: 20%;
left: 30%;
/*pointer-events: none;*/
overflow-y: scroll;
display:none;}
The FullScreen control added to the map is as follows:
var map = new ol.Map({
target: 'map',
layers: [],
controls: [
//Define the default controls
new ol.control.Zoom(),
new ol.control.Attribution(),
//Define some new controls
new ol.control.ZoomSlider(),
new ol.control.MousePosition({
projection: 'EPSG:4326',
coordinateFormat: function (coordinate) {
return 'Coordinates: ' + 'Lat ' + ol.coordinate.format(coordinate, '{y}', 3) + ' Long ' + ol.coordinate.format(coordinate, '{x}', 3);
},
target: 'coordinates'
}),
new ol.control.ScaleLine(),
new ol.control.OverviewMap(),
new ol.control.FullScreen(),
new app.Legend()
],
view: view
});
The problem does not exist in Chrome, but in Firefox. It's because they internally handle fullscreen differently.
Browsers apply fullscreen to a element. OpenLayer's Fullscreen Control selects the map viewport's per default.
However, Firefox seems to hide elements that are not child elements of that selected element, while Chrome does not. Since the popup is not a child of the #map div, you can't see the popup anymore.
OL lets you choose a target element for the Fullscreen control (see api):
var fullscreenTarget = document.getElementById('info').parentElement;
new ol.control.FullScreen({
source: fullscreenTarget
});
Instead of ascending to the parent, give the parent element an id.
PS: You can read more about the Fullscreen API on developer.mozilla.org.
Related
Previously I was working on Phaser 2 but now I need to switch to Phaser 3.
I tried to make the canvas responsive with ScaleManager but it is not working.
I think some of the methods changed but I didn't find any help to rescale the stage full screen.
var bSize = {
bWidth: window.innerWidth ||
root.clientWidth ||
body.clientWidth,
bHeight: window.innerHeight ||
root.clientHeight ||
body.clientHeight,
};
var game;
var canvas = document.getElementById("canvas");
function create() {
// Scaling options
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
// Have the game centered horizontally
game.scale.pageAlignHorizontally = true;
// And vertically
game.scale.pageAlignVertically = true;
// Screen size will be set automatically
game.scale.setScreenSize(true);
}
window.onload = function() {
// Create game canvas and run some blocks
game = new Phaser.Game(
bSize.bWidth, //is the width of your canvas i guess
bSize.bHeight, //is the height of your canvas i guess
Phaser.AUTO,
'frame', { create: create });
canvas.style.position = "fixed";
canvas.style.left = 0;
canvas.style.top = 0;
}
Since v3.16.0, use the Scale Manager. For short:
var config = {
type: Phaser.AUTO,
scale: {
mode: Phaser.Scale.FIT,
parent: 'phaser-example',
autoCenter: Phaser.Scale.CENTER_BOTH,
width: 800,
height: 600
},
//... other settings
scene: GameScene
};
var game = new Phaser.Game(config);
Here is the full code and here are some useful examples using the Scale Manager.
There isn't a scale manager for Phaser 3 yet but it's in development. For now I suggest following this tutorial. It basically centres the canvas with some CSS, then calls a resize function that handles maintaining the game ratio when the resize event is emitted by the window.
Here is the code used in the tutorial linked above:
The css:
canvas {
display: block;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
The resize function:
function resize() {
var canvas = document.querySelector("canvas");
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var windowRatio = windowWidth / windowHeight;
var gameRatio = game.config.width / game.config.height;
if(windowRatio < gameRatio){
canvas.style.width = windowWidth + "px";
canvas.style.height = (windowWidth / gameRatio) + "px";
}
else {
canvas.style.width = (windowHeight * gameRatio) + "px";
canvas.style.height = windowHeight + "px";
}
}
Then:
window.onload = function() {
//Game config here
var config = {...};
var game = new Phaser.Game(config);
resize();
window.addEventListener("resize", resize, false);
}
Try adding max-width to canvas css and then max-height based on aspect ratio. E.g:
canvas {
display: block;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: 100%;
max-height: 50vw;
}
Scale manager will handle assets(Images,Sprites,..,...) positions and size sometime?
I have seen lots of great demos for live graphing of data using D3.
http://bl.ocks.org/simenbrekken/6634070 is one I like. However, all of the examples I have seen use random generated values. I want to graph live data, and display the most recent values as an updating numeric display. I use a python script which writes data from sensor readings to csv files. The csv is 3 values on each line: unixtime,sensor1_value,sensor2_value. Every 5 seconds there is a new line of data added to a ring buffer file which has 720 lines of data. When the web page is displayed I want to read the 720 lines in the buffer file then update the graph with each new value which is written onto the end of the file. I could also create a file with just the new line of csv every 5 seconds so that the update was performed by always reading a file with just 1 line of csv rather than manipulating the entire buffer.
Does anyone know of an example, or the right code to achieve this?
The code for the above cited example is:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.graph .axis {
stroke-width: 1;
}
.graph .axis .tick line {
stroke: black;
}
.graph .axis .tick text {
fill: black;
font-size: 0.7em;
}
.graph .axis .domain {
fill: none;
stroke: black;
}
.graph .group {
fill: none;
stroke: black;
stroke-width: 1.5;
}
</style>
</head>
<body>
<div class="graph"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var limit = 60 * 1,
duration = 750,
now = new Date(Date.now() - duration)
var width = 500,
height = 200
var groups = {
current: {
value: 0,
color: 'orange',
data: d3.range(limit).map(function() {
return 0
})
},
target: {
value: 0,
color: 'green',
data: d3.range(limit).map(function() {
return 0
})
},
output: {
value: 0,
color: 'grey',
data: d3.range(limit).map(function() {
return 0
})
}
}
var x = d3.time.scale()
.domain([now - (limit - 2), now - duration])
.range([0, width])
var y = d3.scale.linear()
.domain([0, 100])
.range([height, 0])
var line = d3.svg.line()
.interpolate('basis')
.x(function(d, i) {
return x(now - (limit - 1 - i) * duration)
})
.y(function(d) {
return y(d)
})
var svg = d3.select('.graph').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height + 50)
var axis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(x.axis = d3.svg.axis().scale(x).orient('bottom'))
var paths = svg.append('g')
for (var name in groups) {
var group = groups[name]
group.path = paths.append('path')
.data([group.data])
.attr('class', name + ' group')
.style('stroke', group.color)
}
function tick() {
now = new Date()
// Add new values
for (var name in groups) {
var group = groups[name]
//group.data.push(group.value) // Real values arrive at irregular intervals
group.data.push(20 + Math.random() * 100)
group.path.attr('d', line)
}
// Shift domain
x.domain([now - (limit - 2) * duration, now - duration])
// Slide x-axis left
axis.transition()
.duration(duration)
.ease('linear')
.call(x.axis)
// Slide paths left
paths.attr('transform', null)
.transition()
.duration(duration)
.ease('linear')
.attr('transform', 'translate(' + x(now - (limit - 1) * duration) + ')')
.each('end', tick)
// Remove oldest data point from each group
for (var name in groups) {
var group = groups[name]
group.data.shift()
}
}
tick()
</script>
</body>
</html>
Whereas the code I use for creating a static graph of one of the values (o2) from my csv versus a line:
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 900 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Set the ranges
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.o2); });
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.csv("./data/buffer.txt", function(error, data) {
data.forEach(function(d) {
d.time = +d.time;
d.o2 = +d.o2;
console.log(d.time);
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.o2; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
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;
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>
I am fairly new to using d3, but what I am trying to do is make a chord diagram of some site traffic, and I am trying to make it interactive by changing the color of the paths when a user clicks on the group for a certain site.here is the style and script section of my code:
<style type="text/css">
.group text {
font: 11px sans-serif;
pointer-events: none;
}
#circle circle {
fill: none;
pointer-events: all;
}
.group path {
stroke: #000;
fill-opacity: .5;
}
path.chord {
stroke-width: .75;
fill-opacity: .75;
}
#circle:hover path.fade {
display: none;
}
</style>
</head>
<body>
<script type="text/javascript">
// Chart dimensions.
var w = 600,
h = 700,
r1 = Math.min(w, h) / 2 - 4,
r0 = r1 - 20,
format = d3.format(",.3r");
// Square matrices, asynchronously loaded; credits is the transpose of sitename.
var sitename = [];
// The chord layout, for computing the angles of chords and groups.
var layout = d3.layout.chord()
.sortGroups(d3.descending)
.sortSubgroups(d3.descending)
.sortChords(d3.descending)
.padding(.04);
// The color scale, for different categories of "worrisome" risk.
var fill = d3.scale.ordinal();
// The arc generator, for the groups.
var arc = d3.svg.arc()
.innerRadius(r0)
.outerRadius(r1);
// The chord generator (quadratic Bézier), for the chords.
var chord = d3.svg.chord()
.radius(r0);
// Add an SVG element for each diagram, and translate the origin to the center.
var svg = d3.select("body").selectAll("div")
.data([sitename])
.enter().append("div")
.style("display", "inline-block")
.style("width", w + "px")
.style("height", h + "px")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
// Load our data file…
d3.csv("data2.csv", function(data) {
var uniqueids = {},
array = [],
n = 0;
// Compute a unique id for each site.
data.forEach(function(d) {
d.siteid1 = uniqueIDMaker(d.siteid1);
d.siteid2 = uniqueIDMaker(d.siteid2);
d.valueOf = value; // convert object to number implicitly
});
// Initialize a square matrix of sitename and users
for (var i = 0; i < n; i++) {
sitename[i] = [];
for (var j = 0; j < n; j++) {
sitename[i][j] = 0;
}
}
// Populate the matrices, and stash a map from id to site.
data.forEach(function(d) {
sitename[d.siteid1.id][d.siteid2.id] = d;
array[d.siteid1.id] = d.siteid1;
array[d.siteid2.id] = d.siteid2;
});
// For each diagram…
svg.each(function(matrix, j) {
var svg = d3.select(this);
// Compute the chord layout.
layout.matrix(matrix);
// Add chords.
svg.selectAll(".chord")
.data(layout.chords)
.enter().append("svg:path")
.attr("class", "chord")
.style("fill", function(d) { return fill(d.source.value); })
.style("stroke", function(d) { return d3.rgb(fill(d.source.value)).darker(); })
.attr("d", chord)
.on("dblclick",function(){
d3.select(this)
.style("fill","red")
.style("stroke","yellow")
})
.append("svg:title")
.text(function(d) { return "site " + d.source.value.siteid2.name + " and site " + d.source.value.siteid1.name + " have " + format(d.source.value) + " common users"; })
;
// Add groups.
var g = svg.selectAll("g.group")
.data(layout.groups)
.enter().append("svg:g")
.attr("class", "group");
// Add the group arc.
g.append("svg:path")
.style("fill", function(d) { return fill(array[d.index]); })
.attr("id", function(d, i) { return "group" + d.index + "-" + j; })
.attr("d", arc)
.append("svg:title")
.text(function(d) { return "site " + array[d.index].name + " has " + format(d.value) + "users"; });
g.append("svg:text")
.attr("x", 6)
.attr("dy", 15)
.filter(function(d) { return d.value > 110; })
.append("svg:textPath")
.attr("xlink:href", function(d) { return "#group" + d.index + "-" + j; })
.text(function(d) { return array[d.index].name; });
});
function uniqueIDMaker(d) {
return uniqueids[d] || (uniqueids[d] = {
name: d,
id: n++
});
}
function value() {
return +this.count;
}});
</script>
any help would be greatly appreciated
http://jsfiddle.net/Rw3aK/2/ is a jsFiddle of the script, not sure how to make it read from a file, so here is the contents of data2.csv:
siteid1,siteid2,count,pubid1,pubid2
8,94,11132,57141,57141
201,94,10035,57141,57141
201,8,9873,57141,57141
0,94,8488,45020,57141
0,8,8258,45020,57141
0,201,7644,45020,57141
0,1,6973,45020,45020
94,1,5719,57141,45020
8,1,5670,57141,45020
1,201,5410,57141,45020
I forked your jsfiddle and converted your CSV data to JSON, now in a variable data: http://jsfiddle.net/mdml/K6FHW/.
I also modified your code slightly so that when you click on a group, all outgoing chords are highlighted red. When you click on a group again, the chords change back to their original color. Here're the relevant snippets:
When adding the chords, label each chord with a class according to the chord's source
svg.selectAll(".chord")
.data(layout.chords)
.enter().append("svg:path")
.attr("class", function(d){ return "chord chord-" + d.source.index; })
...
When clicking a group, check if that group's chords are highlighted.
If so, fill the chords with their default color
If not, fill the chords red
Then record whether or not the group's chords are highlighted in a variable d.chordHighlighted
g.append("svg:path")
...
.attr("id", function (d, i) {
return "group" + d.index + "-" + j;
})
...
.on("click", function(d){
if (d.chordHighlighted)
d3.selectAll(".chord-" + d.index)
.style("fill", fill(d.value));
else{
d3.selectAll(".chord-" + d.index)
.style("fill", "red");
}
d.chordHighlighted = d.chordHighlighted ? false : true;
})