Using D3 for painting grid cells multiple times? - javascript

I have a tsv file with 100000+ lines and 3 columns: time(ascending, nanoseconds);object;color. In this file each object appears multiple times with different color and time. And i have a grid, where each cell represents one of the objects (about 500+) and their actual color.
Here is the Code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
svg {
width: 90%;
height: 400px;
}
</style>
</head>
<body>
<div id="vis">
<button id="play-button" onclick="runMain()" style="margin-left: 2em; margin-top:2em;">Play </button>
<label for="inputDelay">delay:</label>
<input type="text" id="inputDelay" placeholder="20" value="20">
</div>
<svg></svg>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-timer.v1.min.js"></script>
<link type="text/css" href="style.css" rel="stylesheet" />
<script type="text/javascript">
let nodeColors2 = ["#33b5e5", "#cc0000", "#669900", "#b4e56b", "#ffbb33", "#f540ff", "#aa18ff", "#16ffa9", "gray"]
var playButton = d3.select("#play-button");
createSVG();
function createSVG(numParam) {
var svg = d3.select("svg");
var row = svg.selectAll(".row")
.data(createGrid(numParam))
.enter().append("g")
.attr("class", "row");
var column = row.selectAll(".square")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("class", "square")
.attr("id", function(d) {
return "rect" + d.id
})
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.attr("width", function(d) {
return d.width;
})
.attr("height", function(d) {
return d.height;
})
.style("fill", "#fff")
.style("stroke", "#222");
}
/**prescan dataSet for further computation **/
function fileScanning() {
return new Promise(function(resolve, reject) {
let parameterList = [];
setTimeout(() => reject(new Error("setTimeoutError")), 1000);
d3.tsv('testFile.tsv', function(data) {
data.forEach(function(d) {
if (!parameterList.includes(d.param)) {
parameterList.push(d.param);
}
});
resolve(parameterList.sort((a, b) => a - b));
});
});
}
/** create grid: cells get a squareID for parameters **/
function createGrid(numParam = 600) {
var num_columns = 50;
var num_rows = Math.ceil(numParam / num_columns);
var data = new Array();
var xpos = 10; //starting xpos and ypos at 10 so the stroke will show when we make the grid below
var ypos = 10;
var width = 20;
var height = 20;
var squareID = 0;
// iterate for rows
for (var row = 0; row < num_rows; row++) {
data.push(new Array());
// iterate for cells/columns inside rows
for (var column = 0; column < num_columns; column++) {
data[row].push({
x: xpos,
y: ypos,
width: width,
height: height,
id: "" + squareID
});
// each cell gets an ID for further linking to parameters
squareID++;
// increment the x position. I.e. move it over by 50 (width variable)
xpos += width;
}
// reset the x position after a row is complete
xpos = 10;
// increment the y position for the next row. Move it down 50 (height variable)
ypos += height;
}
return data;
}
async function runMain() {
let delay = document.getElementById('inputDelay').value;
d3.select("svg").selectAll(".row").remove();
let parameterList = await fileScanning();
createSVG(parameterList.length);
/** run through dataSet and change nodecolors line for line**/
d3.tsv('testFile.tsv', function(data) {
for (let i = 0; i < data.length; i++) {
setTimeout(() => {
document.querySelector("#rect" + parameterList.indexOf(data[i].param)).setAttribute("style", "fill:" + nodeColors2[data[i].target] + "; stroke: black;");
}, i * delay);
}
});
}
</script>
</html>
The dataset looks like this where object=param and color=target:
time param target
5396736 0 0
21620736 307000 5
36134400 430000 7
38073600 369000 6
39064064 246000 4
39318784 123000 2
48853504 62000 1
58494720 1000 0
65408512 185000 3
87599616 431000 7
87736832 247000 4
90412544 308000 5
91149568 370000 6
96732416 63000 1
96962816 124000 2
116216064 2000 0
117147392 186000 3
i want to start the visualisation (like youtube: variable speed, slider, possible to play/pause, back and forth) and while time is running it should always check the time in the file if an object needs to be updated. so, e.g. beeing at 45 seconds and all objects should represent their latest color update until that time.
As a newbie in general but especially in D3 i am wondering if D3 is a good choice and if so how my approach should look like or if i shouldn't use D3 with these requirements at all? I didn't figure out to fully understand update,enter and exit, yet but it feels like it should be the answer. If so i have no clue how the approach for "rewinding" should look like, since it shouldn't delete an object but repaint it with its latest color before the chosen time.
Here is the latest working example on plunker with a very small dataset for better understanding. It is supposed to look like this (missing the whole slider functionality like in youtube). But it is not supposed to use setTimeout for the moments of painting. I want to use "real time" from the tsv file.
As best fit got these sources: slider, grid.
With slider i am having trouble to change the Date approach into my nanoseconds.
Do you have some help if D3 will work and how to approach all of this since i am a bit lost?
PS: First question for me: Any suggestion for a better title of this question? I am happy to edit my question to make it more comfortable for everyone.

Related

Error displaying geotiff raster data on leaflet map

I hope someone can help me. I am trying to place isolines with labels from a geotiff file onto a leaflet map. The webpage https://geoexamples.com/d3-raster-tools-docs/plot/isolines.html is the example I am looking at, but the problem is that they perform this task on a non-moving leaflet map (no zoom feature). I have found the page https://bost.ocks.org/mike/leaflet/ where he places JSON data over a leaflet map, but does not cover how to transform geotiff data.
I can get the leaflet map to work and zoomable the JSON data remaps correctly but the geotiff data, while it displays, is not remapping correctly in my code and I don't quite understand how to make the isolines resize correctly. I thought it was taken care of with the svg.insert command but it doesn't. I know I am missing a step somewhere but I'm not sure where.
It seems like I am trying to do two things that are not quite explained anywhere combined. I eventually want to use the streamline code, too, which is why I am not using the XML code in the simple isolines code (and I need the labels). I got that version to work fine. I have also tried adding a canvas layer but that didn't work either, but perhaps I did something wrong with that.
I would greatly appreciate any suggestions on this. I think I have provided the necessary code and links to the files.
https://geoexamples.com/d3-raster-tools-docs/code_samples/tz850.tiff
https://geoexamples.com/d3-raster-tools-docs/code_samples/world-110m.json
var map = L.map('map').setView([-0.2858, 60.7868], 3);
mapLink =
'OpenStreetMap';
// Add an SVG element to Leaflet’s overlay pane
var svg = d3.select(map.getPanes().overlayPane).append("svg"),
g = svg.append("g").attr("class", "leaflet-zoom-hide");
d3.request("http://geoexamples.com/d3-raster-tools-docs/code_samples/tz850.tiff")
.responseType('arraybuffer')
.get(function(tiffData) {
d3.json("https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson", function(geoShape) {
// create a d3.geo.path to convert GeoJSON to SVG
var transform = d3.geo.transform({
point: projectPoint
}),
path = d3.geo.path().projection(transform);
// create path elements for each of the features
d3_features = g.selectAll("path")
.data(geoShape.features)
.enter().append("path");
map.on("viewreset", reset);
reset();
// fit the SVG element to leaflet's map layer
function reset() {
bounds = path.bounds(geoShape);
var topLeft = bounds[0],
bottomRight = bounds[1];
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
g.attr("transform", "translate(" + -topLeft[0] + "," +
-topLeft[1] + ")");
// initialize the path data
d3_features.attr("d", path)
.style("fill-opacity", 0.7)
.attr('fill', 'blue');
}
// Use Leaflet to implement a D3 geometric transformation.
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new L.LatLng(y, x));
this.stream.point(point.x, point.y);
}
var tiff = GeoTIFF.parse(tiffData.response);
var image = tiff.getImage();
var rasters = image.readRasters();
var tiepoint = image.getTiePoints()[0];
var pixelScale = image.getFileDirectory().ModelPixelScale;
var geoTransform = [tiepoint.x, pixelScale[0], 0, tiepoint.y, 0, -1 * pixelScale[1]];
var zData = new Array(image.getHeight());
for (var j = 0; j < image.getHeight(); j++) {
zData[j] = new Array(image.getWidth());
for (var i = 0; i < image.getWidth(); i++) {
zData[j][i] = rasters[0][i + j * image.getWidth()];
}
}
var invGeoTransform = [-geoTransform[0] / geoTransform[1], 1 / geoTransform[1], 0, -geoTransform[3] / geoTransform[5], 0, 1 / geoTransform[5]];
var intervalsZ = [1400, 1420, 1440, 1460, 1480, 1500, 1520, 1540];
var linesZ = rastertools.isolines(zData, invGeoTransform, intervalsZ);
var colorScale = d3.scaleSequential(d3.interpolateYlOrRd)
.domain([1400, 1540]);
linesZ.features.forEach(function(d, i) {
svg.insert("path", ".streamline")
.datum(d)
.attr("d", path)
.style("stroke", colorScale(intervalsZ[i]))
.style("stroke-width", "2px")
.style("fill", "None");
});
})
})
<link rel="stylesheet" href="https://d19vzq90twjlae.cloudfront.net/leaflet-0.7/leaflet.css" />
<div id="map" style="width: 600px; height: 400px"></div>
<script src="http://bl.ocks.org/rveciana/raw/bef48021e38a77a520109d2088bff9eb/98a0b74b01109afae76d28bc27c4d1dbc7a87da8/geotiff.min.js"></script>
<script src="http://bl.ocks.org/rveciana/raw/bef48021e38a77a520109d2088bff9eb/98a0b74b01109afae76d28bc27c4d1dbc7a87da8/d3-marching-squares.min.js"></script>
<script src="http://bl.ocks.org/rveciana/raw/bef48021e38a77a520109d2088bff9eb/98a0b74b01109afae76d28bc27c4d1dbc7a87da8/path-properties.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d19vzq90twjlae.cloudfront.net/leaflet-0.7/leaflet.js">
</script>

D3: Combine two colour scales into one

I currently have two variables that I can map to two different colours on two colours scales s1 and s2. s1 gives me the shade of red corresponding to my a value of my variable X (4 different possible colours). s1 gives me the shade of blue corresponding to my a value of my variable Y (4 different possible colours too).
Now what I would like to get is something that allows me to combine these two to get a unique colour for a combination of the variables. So for a pair (X,Y) I get a colour on the scale. So I get a scale of 16 possible colours.
Here is a legend that illustrate the kind of thing I am looking for:
I have been looking at online examples but cannot figure out how to achieve this.
You could combine two threshold scales fairly easily into a new scale function. The core of the function could look like:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
This sets the red and blue channels with the help of two d3 threshold scales. The green is simply set as the average between the two, though you could set that to whatever is desirable, say 0 or the minimum of the two other channels. My red/blue ranges are arbitrary and easily changed as well.
The above could be used as:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(value[0]);
var b = blues(value[1]);
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
return scaleBivariate;
}
// Dummy data:
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
var color = d3.scaleBivariate();
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",function(d) {
return color([d.x,d.y]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>
Of course you might want to add some flexibility by adding methods to modify what datum property sets what colors are associated with what properties, what the thresholds should be, etc. To provide a basic example, the example below has added accessors for setting what property should be mapped to blue and red channels:
d3.scaleBivariate = function() {
function scaleBivariate(value) {
var r = reds(red(value));
var b = blues(blue(value));
return "rgb("+r+","+((r+b)/2)+","+b+")";
}
var blues = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var reds = d3.scaleThreshold()
.range([255,205,155,105,55])
.domain([0,1,2,3,4,5]);
var red = function(d) { return d[0]; }
var blue = function(d) { return d[1];}
// Accessors:
scaleBivariate.red = function(_) {
return arguments.length ? (red = _, scaleBivariate): red;
}
scaleBivariate.blue = function(_) {
return arguments.length ? (blue = _, scaleBivariate): blue;
}
return scaleBivariate;
}
var data = d3.range(16).map(function(d) {
return {x: d%4, y: Math.floor(d/4) }
})
var svg = d3.select("svg");
var size = 30;
// set up the color scale:
var color = d3.scaleBivariate()
.red(function(d) { return d.x; })
.blue(function(d) { return d.y; });
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return d.x * size })
.attr("y", function(d) { return d.y * size })
.attr("width",size)
.attr("height",size)
.attr("fill",color);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.0.0/d3.min.js"></script>
<svg></svg>

d3js v5 + Topojson v3 Show/Hide element on click legend

I would like this reverse action http://bl.ocks.org/d3noob/5d621a60e2d1d02086bf. When I click on an item of my legend, I want focus all elements of the map within these class.
However, how isolate just one class compared to others?
For example in this image, just show elements where the value is included between 23500 and 29000 and hide other elements.
I suggest to 1/ filter data and 2/ fill with the same color these elements but surely it's easier.
Here is my code : https://plnkr.co/edit/ga82Syjc8zxTdAxTVNXu?p=preview
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/d3#5.0.0/dist/d3.min.js"></script>
<script src="https://unpkg.com/topojson#3.0.0/dist/topojson.min.js"></script>
</head>
<svg class="carte_evolution">
<style>
</style>
<body>
<script>
let w = 600;
let h = 600;
let view = [0,0,600,600];
//Dimension
let svgCarteEvolution = d3.select(".carte_evolution")
.attr("width", "100%")
.attr("height", h)
.attr("preserveAspectRatio","xMidYMid meet")
.attr("viewBox", `${view[0]},${view[1]},${view[2]},${view[3]}`);
//Map projection
choro = d3.map();
projection = d3.geoConicConformal()
.center([1.282743, 46.328377])
.scale(2600)
.translate([w / 2, h / 2]);
path = d3.geoPath()
.projection(projection);
//Load csv and topojson
promises1 = d3.json("ze_WGS84_UTF8.topojson");
promises2 = d3.csv("data.csv");
Promise.all([promises1, promises2]).then(function(fr){
//Join csv + topojson
let featureCollection = topojson.feature(fr[0],fr[0].objects.ze_WGS84_UTF8)
for (var i=0; i< fr[1].length;i++){
var csvId = fr[1][i].codgeo;
var csvValue0 = parseFloat(fr[1][i].value0);
var csvYear0 = fr[1][i].year0;
for (var j=0; j<featureCollection.features.length;j++){
var jsonId = featureCollection.features[j].properties.codgeo;
if (csvId === jsonId) {
featureCollection.features[j].properties.value0 = csvValue0;
featureCollection.features[j].properties.year0 = csvYear0;
break;
}
}
}
let color = d3.scaleQuantile()
.range(["#d1e0c9","#b3cea8","#8fb983","#539f53","#008d36"]);
color.domain([
12000,17500,19500,21500,23500,29000
]);
//Map
svgCarteEvolution.append("g")
.selectAll("path")
.data(featureCollection.features)
.enter()
.append("path")
.attr("class", "dep")
.attr("d", path)
.style("fill", function(d){
var value = d.properties["value0"];
return value? color(value):"#ccc";
});
//legend
let legend = svgCarteEvolution.selectAll("g.legend_entree")
.data(color.range().reverse())
.enter()
.append("g")
.attr("class","legend_entree");
legend
.append('rect')
.attr("x", 30)
.attr("y", function(d, i) {
return 65 + i * 20;
})
.attr("width", 20)
.attr("height", 10)
.style("stroke", "black")
.style("stroke-width", 0.1)
.style("fill", function(d){return d;});
//the data objects are the fill colors
legend
.append('text')
.attr("x", 55) //leave 5 pixel space after the <rect>
.attr("y", function(d, i) {
return 65 + i * 20;
})
.attr("dy", "0.8em") //place text one line *below* the x,y point
.style("font-family","latomedium")
.style("font-size","0.9em")
.text(function(d,i) {
var extent = color.invertExtent(d);
//extent will be a two-element array, format it however you want:
var format = d3.format("");
return `${format(+extent[0])} - ${format(+extent[1])}`
});
});
</script>
</body>
</html>
data.csv
"codgeo","year0","value0","year1","value1"
"0050",2012,19162,2014,19698.8
"0051",2012,18501.125,2014,19125.5
"0052",2012,18684.6666666667,2014,19454
"0053",2012,19826,2014,20573.9
"0054",2012,18881.1111111111,2014,19513
"0055",2012,17942.5,2014,18657.3
"0056",2012,19299.0476190476,2014,19703.9
"0057",2012,18873.8095238095,2014,19539.3
"0059",2012,18199,2014,18719
"0060",2012,18921,2014,19563.2
"0061",2012,21259.5238095238,2014,21748.3
"0101",2012,,2014,
"0102",2012,,2014,
"0103",2012,,2014,
"0106",2012,,2014,
"0201",2012,,2014,17250
"0202",2012,,2014,17246.7
"0203",2012,,2014,14972
"0204",2012,,2014,14612.6
"0205",2012,,2014,15617.3
"0206",2012,,2014,13369
"0301",2012,,2014,
"0302",2012,,2014,
"0303",2012,,2014,
"0401",2012,,2014,12522.3
"0402",2012,,2014,15483.8
"0403",2012,,2014,14344.6
"0404",2012,,2014,13134
"0601",2012,,2014,
"1101",2012,23144.7619047619,2014,23464
"1102",2012,22223.5,2014,22729.1
"1103",2012,20637.2222222222,2014,21088
"1104",2012,20402,2014,20901
"1105",2012,21782.7777777778,2014,22274.4
"1106",2012,18304.375,2014,18918
"1107",2012,20038.6666666667,2014,20406.5
"1108",2012,19493,2014,19936.5
"1109",2012,28156.8901098901,2014,28660
"1110",2012,20294.8,2014,20639.5
"1111",2012,22490.6666666667,2014,22812.4
"1112",2012,25996.6666666667,2014,26503.5
"1113",2012,25301.7391304348,2014,25639.6
"1114",2012,21184,2014,21611.9
"1115",2012,20586,2014,20954
"1116",2012,25449.5,2014,25742.4
"1117",2012,22279.3333333333,2014,22665
"1118",2012,19040,2014,19307.2
"1119",2012,22059.0909090909,2014,22519
"2101",2012,17416,2014,18133.9
"2102",2012,18721,2014,19275.4
"2103",2012,19554.7826086957,2014,20204
"2104",2012,20352.8571428571,2014,20892.8
"2105",2012,19630.5555555556,2014,20157.6
"2106",2012,18689,2014,19445.2
"2107",2012,17676.6666666667,2014,18309.1
"2201",2012,19209.5238095238,2014,19791
"2202",2012,17090,2014,17820
"2203",2012,16258.6666666667,2014,17014
"2204",2012,17987.619047619,2014,18675.2
"2205",2012,17320.8333333333,2014,18056
"2206",2012,18163.2,2014,18851
"2207",2012,19732.1428571429,2014,20284.7
"2208",2012,19973.3333333333,2014,20690
"2209",2012,17625,2014,18402
"2210",2012,18778.0952380952,2014,19447.6
"2211",2012,17420,2014,18158.8
"2301",2012,18978.8461538462,2014,19609.3
"2302",2012,19920,2014,20419
"2303",2012,18947,2014,19704.7
"2304",2012,20011.7391304348,2014,20639
"2305",2012,18633.4166666667,2014,19348.6
"2306",2012,19222.6666666667,2014,19937.6
"2307",2012,19716,2014,20349.3
"2401",2012,19496,2014,20116.7
"2402",2012,17587.3913043478,2014,18264.3
"2403",2012,18039.3333333333,2014,18668.9
"2404",2012,20862,2014,21386.8
"2405",2012,19088,2014,19617.5
"2406",2012,19682,2014,20246
"2407",2012,17765,2014,18244
"2408",2012,18813.3333333333,2014,19590.5
"2409",2012,18460.8695652174,2014,19134
"2410",2012,19341.3333333333,2014,20286.2
"2411",2012,18503.5,2014,19170
"2412",2012,20074.7619047619,2014,20669
"2413",2012,19886,2014,20426.5
"2414",2012,18549,2014,19111.7
"2415",2012,19298.0952380952,2014,19929
"2416",2012,19159.5833333333,2014,19763.3
"2417",2012,19013.3333333333,2014,19521.9
"2418",2012,20852,2014,21314
"2419",2012,19696.8,2014,20135.4
"2501",2012,18832.9166666667,2014,19616.7
"2502",2012,19839.6428571429,2014,20447.3
"2503",2012,19357,2014,19949.3
"2504",2012,17890,2014,18668.5
"2505",2012,17836.1904761905,2014,18619.5
"2506",2012,18373.0434782609,2014,19247.3
"2507",2012,18855,2014,19788.7
"2508",2012,18192.0833333333,2014,19076
"2509",2012,19105,2014,19896.6
"2510",2012,18837.2222222222,2014,19621.4
"2511",2012,18071.5,2014,18835
"2512",2012,18059.5,2014,18796.7
"2513",2012,17989.5652173913,2014,18533.5
"2601",2012,20356,2014,21106.7
"2602",2012,18218.4,2014,18926.7
"2603",2012,20719.6666666667,2014,21264.4
"2604",2012,18828.3333333333,2014,19445.7
"2605",2012,17496,2014,18232
"2606",2012,18618.3333333333,2014,19419.2
"2607",2012,18150,2014,18717
"2608",2012,19290,2014,20024
"2609",2012,18376,2014,19020
"2610",2012,18123.5,2014,18997.2
"2611",2012,18232,2014,19093.1
"2612",2012,19023.3962264151,2014,19581.7
"2613",2012,18402.2222222222,2014,19055
"2614",2012,19352.6666666667,2014,19970
"3110",2012,17632,2014,18171.3
"3111",2012,19872.3214285714,2014,20498.6
"3112",2012,17909.5,2014,18786
"3113",2012,19370.8695652174,2014,20029
"3114",2012,17411.6666666667,2014,18153.1
"3115",2012,16527.5,2014,17161.7
"3116",2012,17494,2014,18124.5
"3117",2012,16522.5,2014,17252.5
"3121",2012,18958.5714285714,2014,19749.5
"3122",2012,15848,2014,16471.3
"3123",2012,17325,2014,18070
"3124",2012,17319,2014,18080.7
"3125",2012,16943,2014,17776.5
"3126",2012,17054,2014,17783.8
"3127",2012,17860.6666666667,2014,18527.8
"4101",2012,19057,2014,20396.5
"4102",2012,18167.3913043478,2014,18923.8
"4103",2012,20100,2014,20734.3
"4104",2012,19253.4615384615,2014,19914.7
"4105",2012,18178,2014,18899.6
"4106",2012,18415.5555555556,2014,19180
"4107",2012,19765.3333333333,2014,20611.4
"4108",2012,17936.3043478261,2014,18670.5
"4109",2012,19220,2014,19884
"4110",2012,19699,2014,20456
"4111",2012,19975.5555555556,2014,21490
"4112",2012,18561.9047619048,2014,19239.6
"4113",2012,18533,2014,19207
"4114",2012,17932,2014,18625
"4115",2012,18513.6,2014,19304
"4201",2012,21662,2014,22259.4
"4202",2012,22121.4285714286,2014,22678
"4203",2012,20694.9743589744,2014,21249.2
"4204",2012,20996.6666666667,2014,21554
"4205",2012,20637.2,2014,20939.5
"4206",2012,22284.4,2014,22998
"4207",2012,21402,2014,21892
"4208",2012,20530.4761904762,2014,20987.7
"4209",2012,27116,2014,27578
"4301",2012,19866.7857142857,2014,20474.7
"4302",2012,19037.6923076923,2014,19688.7
"4303",2012,26342.380952381,2014,26900.4
"4304",2012,23223.3333333333,2014,24219
"4305",2012,19166.4516129032,2014,19840
"4306",2012,19092.2222222222,2014,19879.5
"4307",2012,20652,2014,21500.8
"4308",2012,18128,2014,18743
"4309",2012,18185,2014,18844.7
"5201",2012,18881.4814814815,2014,19667.6
"5202",2012,17665.6,2014,18286
"5203",2012,20616,2014,21241
"5204",2012,19844.8,2014,20508
"5205",2012,19443,2014,20094.4
"5206",2012,18905.7142857143,2014,19605
"5207",2012,17964.6666666667,2014,18666
"5208",2012,18107.3333333333,2014,18734.4
"5209",2012,18951.7857142857,2014,19703.8
"5210",2012,18635.7142857143,2014,19332
"5211",2012,18973.2258064516,2014,19556.5
"5212",2012,18178.6666666667,2014,18824
"5213",2012,19304.6666666667,2014,19966.7
"5214",2012,18363.3333333333,2014,19098.9
"5215",2012,19146.6666666667,2014,19786.7
"5216",2012,18103.3333333333,2014,18876.7
"5217",2012,18984,2014,19686
"5218",2012,19186.1904761905,2014,19813.8
"5219",2012,20042.380952381,2014,20712.9
"5301",2012,19939.2,2014,20655.3
"5302",2012,18221.4285714286,2014,18960
"5303",2012,20016,2014,20691.3
"5304",2012,18641.5,2014,19282.2
"5305",2012,19581.3333333333,2014,20232.9
"5306",2012,19916.0869565217,2014,20649.4
"5307",2012,17941.3333333333,2014,18671
"5308",2012,19373.3333333333,2014,19991.9
"5309",2012,19634.347826087,2014,20377.6
"5310",2012,18645.3846153846,2014,19283.8
"5311",2012,18720,2014,19397.2
"5312",2012,20578,2014,21143.8
"5313",2012,19380.6666666667,2014,20159.3
"5314",2012,19439,2014,20248.1
"5315",2012,19573,2014,20304.3
"5316",2012,18751.25,2014,19417.2
"5317",2012,18940,2014,19701.3
"5318",2012,20075.4166666667,2014,20740
"5401",2012,17907.5,2014,18522.9
"5402",2012,18704.6666666667,2014,19349
"5403",2012,18580.3846153846,2014,19280
"5404",2012,17407,2014,18222
"5405",2012,19327.5,2014,20055.3
"5406",2012,18512.4,2014,19128
"5407",2012,19217.6,2014,19903.5
"5408",2012,18673,2014,19379.3
"5409",2012,20270,2014,20855.6
"5410",2012,19489.1666666667,2014,20238.1
"5411",2012,17890,2014,18620.5
"5412",2012,18078.0952380952,2014,18834
"5413",2012,19446.5,2014,20043
"7201",2012,18008.4637681159,2014,18697.3
"7202",2012,18425,2014,19159
"7203",2012,18106.6666666667,2014,18658.7
"7204",2012,20667,2014,21249.5
"7205",2012,18035.3333333333,2014,18711.3
"7206",2012,18450.9523809524,2014,19101.3
"7207",2012,20320.8695652174,2014,20948
"7208",2012,19536,2014,20100
"7209",2012,18869.3333333333,2014,19480.7
"7210",2012,17737.5,2014,18362
"7211",2012,17410,2014,17987.6
"7212",2012,20164.6666666667,2014,20683.9
"7213",2012,19245,2014,19834
"7214",2012,20302,2014,20867.6
"7301",2012,18256,2014,18876.3
"7302",2012,17957,2014,18514
"7303",2012,18034.6666666667,2014,18724.8
"7304",2012,19017.2222222222,2014,19799.3
"7305",2012,17916.1904761905,2014,18698
"7306",2012,18578,2014,19336.7
"7307",2012,18857.619047619,2014,19230
"7308",2012,18793.3333333333,2014,19457.6
"7309",2012,19058.6956521739,2014,19787.6
"7310",2012,18895.3333333333,2014,19429.5
"7311",2012,18878.1780538302,2014,19430.7
"7312",2012,18183.8461538462,2014,18879.2
"7313",2012,18197.3333333333,2014,18632
"7401",2012,19040,2014,19716
"7402",2012,18143.3333333333,2014,18897.2
"7403",2012,17301.3043478261,2014,17984
"7404",2012,19118,2014,19698
"8201",2012,20643.8095238095,2014,21227.1
"8202",2012,20174.6666666667,2014,20682.4
"8203",2012,18474.4444444444,2014,19119.6
"8204",2012,18964.6153846154,2014,19694
"8205",2012,17833.8461538462,2014,18486
"8206",2012,18796,2014,19433
"8207",2012,18750.4347826087,2014,19414.5
"8208",2012,19158.8,2014,19833.1
"8209",2012,20058.9655172414,2014,20741.1
"8210",2012,21119.1153846154,2014,21684.5
"8211",2012,19926,2014,20499.3
"8212",2012,18585.7142857143,2014,19272
"8213",2012,21155,2014,21768
"8214",2012,20965,2014,21470
"8215",2012,20356.9230769231,2014,20849.2
"8216",2012,21009.6,2014,21576.5
"8217",2012,20380.7692307692,2014,20996
"8218",2012,23005.2173913043,2014,23712.7
"8219",2012,27201.9230769231,2014,27749.3
"8220",2012,21017,2014,21626.5
"8221",2012,21245.0909090909,2014,21706.7
"8222",2012,23036.0869565217,2014,23862.9
"8301",2012,18453.9393939394,2014,19023.3
"8302",2012,18534.3333333333,2014,19190
"8303",2012,18492,2014,19091.3
"8304",2012,18855.7142857143,2014,19606.7
"8305",2012,17307.5595238095,2014,18159
"8306",2012,17459.1666666667,2014,18457
"8307",2012,17890.4761904762,2014,18484.8
"8308",2012,18963,2014,19466.1
"8309",2012,18347.1428571429,2014,19084
"8310",2012,20412,2014,21071.7
"8311",2012,19008.5,2014,19795.7
"8312",2012,18111.6666666667,2014,18856.5
"9101",2012,17408.5714285714,2014,17945.3
"9102",2012,16617.3333333333,2014,17173
"9103",2012,17123.3333333333,2014,17661
"9104",2012,16840,2014,17349.6
"9105",2012,18586,2014,19386.7
"9106",2012,18106.6666666667,2014,18625
"9107",2012,17128.4615384615,2014,17763
"9108",2012,16940.8695652174,2014,17503.5
"9109",2012,17352,2014,17867.5
"9110",2012,16858,2014,17391.3
"9111",2012,19566.6666666667,2014,20024.4
"9112",2012,17690.4,2014,18320.7
"9113",2012,18336.5217391304,2014,19110
"9114",2012,18100.4545454545,2014,18640.7
"9115",2012,17447.2222222222,2014,17903.9
"9116",2012,17108,2014,17654.6
"9301",2012,18502,2014,19034.2
"9302",2012,18934.8,2014,19401.5
"9303",2012,18952.3076923077,2014,19365.3
"9304",2012,18794.7619047619,2014,19335
"9305",2012,20466.1111111111,2014,20830.8
"9306",2012,20292.8,2014,20668.7
"9307",2012,19959,2014,20333.3
"9308",2012,21603.3333333333,2014,22211.1
"9309",2012,18084,2014,18679.5
"9310",2012,19009,2014,19507
"9311",2012,19514.6666666667,2014,20090
"9312",2012,19813.6,2014,20490.6
"9313",2012,18840,2014,19254.8
"9314",2012,19946.0357142857,2014,20238
"9315",2012,19492,2014,20070.5
"9316",2012,17739.1304347826,2014,18245.7
"9317",2012,17904,2014,18510.6
"9401",2012,19633.8461538462,2014,20418.3
"9402",2012,17570,2014,17890
"9403",2012,17200.8333333333,2014,17640.5
"9404",2012,17777.2666666667,2014,18376.4
"9405",2012,17254.5,2014,17473.5
"9406",2012,17553.3333333333,2014,18426
"9407",2012,16885,2014,17577.7
Your instincts will work - filter the path data according to the color:
features.filter(function(feature) {
return color(feature.properties["value0"]) == d;
})
I saved your feature paths in the variable features in the plunkrs below
As the datum of each legend entry is a color, we can just filter the paths based on which datum would produce the same color when scaled. We could alternatively give class names or apply other indicators to filter.
Color the filtered paths on the event, revert all paths on some sort anti-event. Since you haven't said what will reset the map, I'll assume if you click on the same legend entry twice in a row the map should reset (this lets you toggle between each legend entry). To do so we'll need to keep track of which legend entry is currently in focus:
var highlighted = "";
Then we just listen for a click event on the legend entry and:
.on("click",function(d,i) {
highlighted = ""; //reset
// revert map back to default state
}
else {
highlighted = d;
// filter features to highlight certain ones.
})
Altogether that might look like:
.on("click",function(d,i) {
// clicking an active entry: reset:
if(highlighted == d) {
// reset opacity on each path:
features.style("opacity",1);
// reset legend entries' fills:
legend.selectAll("rect")
.style("fill",function(d) { return d; });
// reset highlight variable since nothing is highlighted
highlighted = "";
}
// clicking a different entry: highlight that entry:
else {
//update highlighted variable
highlighted = d;
// set opacity low for all features, filter for chosen features and style accordingly:
features.style("opacity",0.2)
.filter(function(f) {
return color(f.properties["value0"]) == d;
})
.style("opacity",1);
// hollow legend entries
legend.selectAll("rect")
.style("fill","white")
// fill selected option:
d3.select(this).select("rect")
.style("fill",function(d) { return d;});
}
})
Which looks something like:
(I'm formatting the legend entries to show what is highlighted - I changed your stroke to accomodate this)
Here's an updated plunkr.
If you want to be able to toggle multiple legend items at once, then the logic gets a bit more detailed and the highlighted variable becomes an array
As an alternative with mouseover/off events rather than clicks (so no need to track what is actively shown), here's a slightly different approach using the same principles: plunkr

Display Data from JSON .on("mouseover", How?

What I'm trying to do is get a function that when I move my mouse over the Map(USA) that is drawn with a d3.json file that this will show it's data which is stored in a CSV file.
What I'm having trouble with is that I don't know how to implement the mouseover function, because I don't know where I should reference towards.
This is my code. I have no idea how to do this. Any tips would be really appreciated.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Applying a projection to SVG paths</title>
<script type="text/javascript" src="../d3/d3.v3.js"></script>
<style type="text/css">
/* No style rules here yet */
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 300;
//Define map projection
var projection = d3.geo.albersUsa()
.translate([w/2, h/2])
.scale([500]);
//Define path generator
var path = d3.geo.path()
.projection(projection);
//Define quantize scale to sort data values into buckets of color
var color = d3.scale.quantize()
.range(["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"]);
//Colors taken from colorbrewer.js, included in the D3 download
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Load in agriculture data
d3.csv("us-ag-productivity-2004.csv", function(data) {
//Set input domain for color scale
color.domain([
d3.min(data, function(d) { return d.value; }),
d3.max(data, function(d) { return d.value; })
]);
//Load in GeoJSON data
d3.json("us-states.json", function(json) {
//Merge the ag. data and GeoJSON
//Loop through once for each ag. data value
for (var i = 0; i < data.length; i++) {
//Grab state name
var dataState = data[i].state;
//Grab data value, and convert from string to float
var dataValue = parseFloat(data[i].value);
//Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
//Copy the data value into the JSON
json.features[j].properties.value = dataValue;
//Stop looking through the JSON
break;
}
}
}
//Bind data and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("fill", function(d) {
//Get data value
var value = d.properties.value;
if (value <1.15) {
//If value exists…
return color(value);
} else if (value >1.15) {
return "red"; }
else {
//If value is undefined…
return "#ccc";
}
});
});
});
</script>
</body>
</html>
You must add 3 events mousedown, mousemove, mouseup
const target = document.getElementsByTagName("SVG");
// or by id
const target = document.getElementsById("someid");
target.addEventListener('mousedown', () => {
console.log('mousedown');
});
target.addEventListener('mousemove', () => {
console.log('mousemove');
});
target.addEventListener('mouseup', () => {
console.log('mouseup');
});
but I thing d3.js handle all this, check the official documentation.

d3: Adding and removing force.nodes based on slider values

I had some difficulty a while back adding and removing nodes based on user input which was solved by updating the entire set of objects pushed into force.nodes() each time the user selected which they wanted to view from a list of checkboxes.
Updating from a slider however I think requires a more finesse touch - I don't want to update the entire set every time the slider is moved. I want to push and pop nodes in and out of force.nodes().
With my current code the nodes are coming out just fine - they're just not going back in. jsfiddle here - https://jsfiddle.net/hiwilson1/ancmtxux/3/
This is the part causing problems;
function brushed() {
var exists;
//var newd = new Date(2013, 05, 01)
data.forEach(function(d, i) {
//if data point in range (between extent 0 and 1)
if (d.date >= brush.extent()[0] && d.date <= brush.extent()[1]) {
exists = force.nodes().some(function(node, i) {
//check if data point already exists in force.nodes()
return (node.mIndex == d.mIndex)
})
console.log(exists)
if (!exists) {
force.nodes().push(d)
}
}
else {
force.nodes().splice(i, 1)
}
})
d3.select("#nodeCount").text(force.nodes().length)
}
For each data point, I'm checking whether or not the point lies between extent()[0] and [1]. If it does, then check force.nodes() to see if it's currently a member there. If it isn't, then push it into force.nodes().
If the data point doesn't lie between the extents then splice it from force.nodes(). This last bit works just fine.
UPDATE: Fixed. I actually also worked out how to filter links attached to nodes as well. jsfiddle here - https://jsfiddle.net/hiwilson1/7oumeat5/2/. Never try and do this with indexes/hard coded indexes, compare the nodes/links as objects.
Incidentally I'm seeing links over the top of nodes. If there's some way to fix this I'd be happy to hear it.
FURTHER UPDATE: To ensure links behind nodes use .insert("line", ":first-child") in place of .append("line")
There are a few problems with your current code. The fundamental problem is that you're giving data to force.nodes(), which means that the two data structures are actually the same. That is, when you're removing elements from force.nodes(), you're modifying the underlying data as well. Hence you can't add them back -- they're gone.
This is easily fixed by passing a copy of data to force.nodes():
var force = d3.layout.force()
.nodes(JSON.parse(JSON.stringify(data)))
Then you're removing the wrong nodes from force.nodes() -- the index you're using is for data, not force.nodes(). You can compute the index of the data element in force.nodes() and use it like this:
data.forEach(function(d, i) {
var idx = -1;
force.nodes().forEach(function(node, j) {
if(node.mIndex == d.mIndex) {
idx = j;
}
});
//if data point in range (between extent 0 and 1)
if (d.date >= brush.extent()[0] && d.date <= brush.extent()[1]) {
if (idx == -1) {
force.nodes().push(d)
}
}
else if(idx > -1) {
force.nodes().splice(idx, 1)
}
Finally, you need to call force.start() at the end of brushed for the changes to become visible after the layout has settled down.
Complete example here.
Building on the answer from the mighty #Lars Kotthoff, who fixed your technical problem, I will focus on the architecture. Here is an architecture that is simpler and more in keeping with d3 idiom.
The main principles are:
Use Array.prototype.filter() to manage the data
Use standard, data driven patterns.
Drive the visualisation by managing the data and then use the standard, general update pattern to drive the changes into the vis.
Seperate data events and animation events.
Respond to the data changes only when need... do it in the brushed event, not on every tick. The brushed event is a data event and the tick routine is an animation event. It's not optimal to be managing data on animation events on the off chance that it is required.
Make the entry and exit of the nodes a bit smoother.
The benefit of point 1 is that the filtered array is actually an array of reference to the original data elements, so when the extra state is added on the copied array, it is actually added on the original data array. Thus, the previous state is available when it is filtered back in, hence the smooth exit and entry behaviour. Meanwhile, no elements are deleted in the original data when the brush filters down: only the references to them are deleted in the cloned array. I have to admit I had not expected that but it is a nice discovery, even if by accident!
Of course this only works because the array elements are objects.
working example here...
var width = 700,
height = 600,
padding = 20;
var start = new Date(2013, 0, 1),
end = new Date(2013, 11, 31)
var data = []
for (i = 0; i < 100; i++) {
var point = {}
var year = 2013;
var month = Math.floor(Math.random() * 12)
var day = Math.floor(Math.random() * 28)
point.date = new Date(year, month, day)
point.mIndex = i
data.push(point)
}
var force = d3.layout.force()
.size([width - padding, height - 100])
.on("tick", tick)
.start()
var svg = d3.select("body").append("svg")
.attr({
"width": width,
"height": height
})
//build stuff
var x = d3.time.scale()
.domain([start, end])
.range([padding, width - 6 * padding])
.clamp(true)
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.tickPadding(20)
//.tickFormat(d3.time.format("%x"))
var brush = d3.svg.brush()
.x(x)
.extent([start, end])
.on("brush", brushed1)
//append stuff
var slidercontainer = svg.append("g")
.attr("transform", "translate(100, 500)")
var axis = slidercontainer.append("g")
.call(xAxis)
var slider = slidercontainer.append("g")
.call(brush)
.classed("slider", true)
//manipulate stuff
d3.selectAll(".resize").append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 10)
.attr("fill", "Red")
.classed("handle", true)
d3.select(".domain")
.select(function () { return this.parentNode.appendChild(this.cloneNode(true)) })
.classed("halo", true)
function brushed1(e) {
var nodes = includedNodes(data, brush);
nodes.enter().append("circle")
.attr("r", 10)
.attr("fill", "red")
.call(force.drag)
.attr("class", "node")
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
nodes
.exit()
.remove()
force
.nodes(includedData(data, brush))
.start()
}
function includedData(data, brush) {
return data.filter(function (d, i, a) {
return d.date >= brush.extent()[0] && d.date <= brush.extent()[1]
})
}
function includedNodes(data, brush) {
return svg.selectAll(".node")
.data(includedData(data, brush), function (d, i) {
return d.mIndex
})
}
function tick() {
includedNodes(data, brush)
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
}
brushed1()
.domain {
fill: none;
stroke: #000;
stroke-opacity: .3;
stroke-width: 10px;
stroke-linecap: round;
}
.halo {
fill: none;
stroke: #ddd;
stroke-width: 8px;
stroke-linecap: round;
}
.tick {
font-size: 10px;
}
.selecting circle {
fill-opacity: .2;
}
.selecting circle.selected {
stroke: #f00;
}
.handle {
fill: #fff;
stroke: #000;
stroke-opacity: .5;
stroke-width: 1.25px;
cursor: crosshair;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<p id="nodeCount"></p>

Categories