D3 Datum Update Boxplot - svg

I am trying to update 3 svgs (BoxPlots). In the following code the var svg contains an array of the 3 svgs and the _data_ has been updated to the correct number of records. I follow the BoxPlot Example , but cannot see what I am doing wrong.
$('#Records li a ').click(function() {
var id = event.target.id;
var idparts = id.split("_");
var numrec = idparts[1];
d3.json("./php/connection2.php?numrecs="+numrec, function (error, csv) {
d3.json("./php/connection2.php?numrecs="+numrec, function (error, csv) {
var chart = d3.box()
.whiskers(iqr(1.5))
.width(width)
.height(height);
var numericArray = createdata(csv);
chart.domain([min, max]);
var svg = d3.selectAll("svg")
svg.data(numericArray).call(chart.duration(1000));
});
$('#RecordsDropdown').removeClass("open");
return false;
});
});
Ive also tried:
.....
thisdata=numericArray;
var svg = d3.selectAll("svg");
svg.data(thisdata);
svg.call(chart.duration(1000));
//updatedata(svg);
I am getting this error when it tries to create new outliers
Uncaught Error: NotFoundError: DOM Exception 8

I found that if you remove the insert("text") at line ~158 in box.js:
//change this:
//outlier.enter().insert("circle", "text")
// to this:
outlier.enter().insert("circle")
That will prevent the Uncaught Error: NotFoundError: DOM Exception 8
The only other thing that I had to change was to make sure that it appended the new outlier in the g of my box plot when I called it to animate it. Otherwise they'll be off a bit.
var svg = d3.selectAll(".box" + Attr + sample + " g")
.data(data);
svg.call(chart.duration(1000));
I don't know what the "text" was there for.

Related

Getting the width of SVG text with a title on Node

I'm using svg.js and svgdom on Node to generate SVG files. Here's my code:
// set up svg.js and svgdom
const { createSVGWindow } = require('svgdom');
const window = createSVGWindow();
const document = window.document;
const { SVG, registerWindow } = require('#svgdotjs/svg.js');
registerWindow(window, document);
const draw = SVG(document.element).size(100, 100);
// generate SVG text and title
var title = draw.element('title').words('noun');
var text = draw.text('text').add(title);
var width = text.length();
That last line throws TypeError: cannot read property 'style' of null in svgdom/main-require.cjs:3721:38.
This works when I do it in the browser; something about moving to Node and svgdom causes it to fail. Is there another way I can calculate the width of this text element, or a different way to attach the title to the text? (The title is being used to show additional information on hover.) Calling text.width() always returns 0.
I'm using svg.js 3.0.16 and svgdom 0.1.8. Should I open an issue on one of them? If so, which one?

Why does my for loop only goes through once when i call function inside it?

I got list of videos from API, it has list of urls fo thumbnail and i would like to combine thumbnails of each video to gif. When i loop through videos and don't generate gifs it goes through 5 times as expected, but if i include function that should generate gifs it only goes through once, without any errors. I have no idea what is happening
I'm using node.js, discord.js, get pixels and gif-encoder modules to generate thumbnails.
for(i=0;i<5;i++){
generateThumbnail(data[i].video.video_id,data[i].video.thumbs,function(){
var tags = '';
for(t=0;t<data[i].video.tags.length;t++){
tags = tags + data[i].video.tags[t].tag_name+', ';
}
fields = [
{name:data[i].video.title,
value:value},
{name:'Tags',
value:tags}
]
msg.channel.send({embed: {
color: 3447003,
thumbnail: {
"url": ""
},
fields: fields,
}});
});
}
function generateThumbnail(id,images,fn){
var pics = [];
console.log(id)
var file = require('fs').createWriteStream(id+'.gif');
var gif = new GifEncoder(images[0].width, images[0].height);
gif.pipe(file);
gif.setQuality(20);
gif.setDelay(1000);
gif.setRepeat(0)
gif.writeHeader();
for(i=0;i<images.length;i++){
pics.push(images[i].src)
}
console.log(pics)
addToGif(pics,gif);
fn()
}
var addToGif = function(images,gif, counter = 0) {
getPixels(images[counter], function(err, pixels) {
gif.addFrame(pixels.data);
gif.read();
if (counter === images.length - 1) {
gif.finish();
} else {
addToGif(images,gif, ++counter);
}
})
}
if i dont use GenerateThumbnail function it goes through 5 times as expected and everything works fine, but if i use it it goes through only once, and generated only 1 gif
Use var to declare for vars. Ie for(var i=0....
If you declare vars without var keyword, they are in the global scope. ..... and you are using another i var inside the function but now it is the same var from the outer for loop.

Convert geojson polygons into points with node using query-overpass and turf.js

I use the node module "query-overpass" for a query to get farmshops from openstreetmaps. I would like to convert all polygons to points inside this script. I use turf.js to get the centroids of theese polygons, but I am not able to change the objects in a permanent way. This is my code so far:
const query_overpass = require("query-overpass");
const turf = require ("turf");
const fs = require("fs")
let test
let filename = "data/test.js"
let bbox = "48.91821286473131,8.309097290039062,49.0610446187357,8.520584106445312";
console.log('starting query for ' +filename)
console.log('bbox: ' +bbox)
let query = `
[out:json][timeout:250];
// gather results
(
// query part for: “vending=milk”
node["vending"="milk"](${bbox});
way["vending"="milk"](${bbox});
relation["vending"="milk"](${bbox});
// query part for: “shop=farm”
node["shop"="farm"](${bbox});
way["shop"="farm"](${bbox});
relation["shop"="farm"](${bbox});
// query part for: “vending=food”
node["vending"="food"](${bbox});
way["vending"="food"](${bbox});
relation["vending"="food"](${bbox});
);
// print results
out body;
>;
out skel qt;
`;
// query overpass, write result to file
query_overpass(query, (error, data) => {
data = JSON.stringify(data , null, 1)
console.log(data)
test = JSON.parse(data)
//create centroids for every polyon and save them as a point
for (var i = 0; i < test.features.length; i++) {
console.log("Log: " +test.features[i].geometry.type)
console.log("Log: " +test.features[i].properties.name)
if (test.features[i].geometry.type === "Polygon"){
console.log("polygon detected")
var centroid = turf.centroid(test.features[i]);
var lon = centroid.geometry.coordinates[0];
var lat = centroid.geometry.coordinates[1];
console.log(" lon: " +lon +" lat: " +lat)
test.features[i].geometry.type = 'Point'
//delete Polygon structure and insert centroids as new points here
console.log("polygon deleted and changed to point")
}
}
console.log(test)
fs.writeFile(filename, `var file = ${test};` , ["utf-8"], (error, data) => {if (error) {console.log(error)}})
}, {flatProperties: true}
)
It seems like I can change things inside of the for loop, but they do not appear when the data is saved later. It is basically a question of how to edit json objects properly, but I can't figure out why this doesnt work here at all.
So there are basically two questions:
Why cant I override geometry.type in the example above?
How can I delete the old polygon and add a new point to a feature?
Thanks for any help.
That's quite complicated... Why don't you let Overpass API do this job and use out center; instead of out body;>;out skel qt; to return the center points of all nodes, ways and relations. You can use overpass-turbo.eu to try this out first.

How to use D3 force layout with existing SVG elements as nodes

I have a javascript array of objects where each object has .ui attribute that contains an existing SVG shape/element of various kinds ( a self contained "g" element with others inside ).
I want to arrange these objects as a force graph layout in D3. So, instead of having d3 create circles (or whatever) I want to assign one of my ui objects to the graph.
I have created a simplified fiddle << HERE >> for that. The objects that are pre-existing and should be part of the graph are the 3 colored rectangles. In this example I failed to achieve that. The d3 append command can be used just to construct shapes/elements, but not to append an already constructed one. I was trying to do this, and it cannot work in D3:
node.append( function(d) { d.ui.node(); }) ...
... as mentioned, this wont work as d3 does not append elements.
var svgContainer = d3.select("#svgContainer");
// =============================================================
// creates the SVG elements to be used ( 3 colored rectangles )
// =============================================================
var element0a = svgContainer.append("g").attr("transform","translate(100,100)");
var element0b = element0a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","red");
var element1a = svgContainer.append("g").attr("transform","translate(100,200)");
var element1b = element1a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","green");
var element2a = svgContainer.append("g").attr("transform","translate(100,300)");
var element2b = element2a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","blue");
// =============================================================
// fills up an object array that contains details plus the UI attribute
// =============================================================
var nodeArray = new Array();
nodeArray[0] = { id : "000", label : "label 000", ui : element0a };
nodeArray[1] = { id : "001", label : "label 001", ui : element1a };
nodeArray[2] = { id : "002", label : "label 002", ui : element2a };
// not interested in dealing with the edge/link right now, just empty
var linkArray = new Array();
// =============================================================
// D3 force layout stuff
// =============================================================
var force = self.force = d3.layout.force()
.nodes(nodeArray)
.links(linkArray)
.gravity(.05)
.distance(80)
.charge(-100)
.size([600, 600])
.start();
var node = svgContainer.selectAll("g.node")
.data(nodeArray)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
// HERE (below) COMES THE ISSUE, where you see append("cicle") I want to append the nodeArray's ".ui" element.
node.append("circle") // <---
.attr("class", "node")
.attr("r",10)
.attr("fill","orange")
.call(force.drag);
force.on("tick", function() {
node.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")";});
});
Based on advice from Lars Kotfoff :
Working fiddle here
If the elements to be graph-ed already exist, simply skip the enter() D3 phase and use select() or selectAll() based on a common characteristic of your elements ( a class name for instance ).
This is how the element got created ( added a specific class for allowing an isolated selection ):
var element0a = svgContainer.append("g").attr("class","node").attr("transform","translate(100,100)");
var element0b = element0a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","red");
This is thene part of the code replacing the enter() phase
var node = svgContainer.selectAll("g.node")
.data(nodeArray)
.call(force.drag);
This is what was removed
var node = svgContainer.selectAll("g.node")
.data(nodeArray)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
// HERE (below) COMES THE ISSUE, where you see append("cicle") I want to append the nodeArray's ".ui" element.
node.append("circle") // <---
.attr("class", "node")
.attr("r",10)
.attr("fill","orange")
.call(force.drag);
The problem is that you append the elements to svgContainer to create them, thus they are already attached to something.
What I would suggest as a workaround is to store the element properties in a json file and then read the configuration from the json.
There might be something more elegant.

RaphaelJS scale issue

Can someone help me on this? I tried to shrink the size of the svg to half of its original size.
Here is the JSfiddle link
http://jsfiddle.net/wildleaf/ntzf3/
It runs fine with the original but if I uncomment the last line, the size is reduced to half but shape is changed.
You need to add transformation, not overwrite it.
See updated fiddle http://jsfiddle.net/ntzf3/1/
Code
...
var rsr = Raphael("canvas", 800, 400);
var elements = rsr.set();
for (var room in floorData) {
var r = floorData[room];
var ele = rsr.add([r]);
elements.push(ele);
if (r.data) {
ele.data('room', r.data['room']);
}
}
elements.forEach(function (el) {
var e = el[0];
e.transform(e.transform() + "s0.5,0.5,0,0");
});
Figured it out. It is overwriting the transform information in the original graph. I changed the last line to elements.transform("...s0.5,0.5,0,0") then everything works.

Resources