Plot data from javascript function using Vega - vega

I want to plot a function interactively (i.e. the function has a parameter that can change) using Vega or Vega-Lite. As far as I can tell the data parameter can only be from a file or object.
Obviously I can recreate the entire graph/spec every time the function parameter changes, but I'd rather just update the data, so the entire graph doesn't need to be re-rendered. Is there a way to do that?
My function is too complex for Vega's built-in expression system so please don't suggest that.

You can do this using the Vega view API. Here is an example of a script that inserts dynamically-generated data into a Vega-Lite chart:
var spec = {
$schema: 'https://vega.github.io/schema/vega-lite/v3.json',
data: {name: 'table'},
width: 400,
mark: 'line',
encoding: {
x: {field: 'x', type: 'quantitative', scale: {domain: [0, 100]}},
y: {field: 'y', type: 'quantitative', scale: {domain: [-1, 1]}}
}
};
function makeData(N) {
data = [];
for (x = 0; x < N; x++) {
data.push({x: x, y: Math.sin(x / 10)})
}
return data
}
vegaEmbed('#chart', spec).then(function(res) {
var changeSet = vega.changeset().insert(makeData(100));
res.view.change('table', changeSet).run();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega/5.7.0/vega.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega-lite/3.4.0/vega-lite.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega-embed/5.1.3/vega-embed.js"></script>
<div id="chart"></div>

Related

html2pdf fit image to pdf

I finally got my html2pdf to work showing my web page just how I want it in the pdf(Any other size was not showing right so I kept adjusting the format size until it all fit properly), and the end result is exactly what I want it to look like... EXCEPT even though my aspect ratio is correct for a landscape, it is still using a very large image and the pdf is not standard letter size (Or a4 for that matter), it is the size I set. This makes for a larger pdf than necessary and does not print well unless we adjust it for the printer. I basically want this exact image just converted to a a4 or letter size to make a smaller pdf. If I don't use the size I set though things are cut off.
Anyway to take this pdf that is generated and resize to be an a4 size(Still fitting the image on it). Everything I try is not working, and I feel like I am missing something simple.
const el = document.getElementById("test);
var opt = {
margin: [10, 10, 10, 10],
filename: label,
image: { type: "jpeg", quality: 0.98 },
//pagebreak: { mode: ["avoid-all", "css"], after: ".newPage" },
pagebreak: {
mode: ["css"],
avoid: ["tr"],
// mode: ["legacy"],
after: ".newPage",
before: ".newPrior"
},
/*pagebreak: {
before: ".newPage",
avoid: ["h2", "tr", "h3", "h4", ".field"]
},*/
html2canvas: {
scale: 2,
logging: true,
dpi: 192,
letterRendering: true
},
jsPDF: {
unit: "mm",
format: [463, 600],
orientation: "landscape"
}
};
var doc = html2pdf()
.from(el)
.set(opt)
.toContainer()
.toCanvas()
.toImg()
.toPdf()
.save()
I have been struggling with this a lot as well. In the end I was able to resolve the issue for me. What did the trick for me was setting the width-property in html2canvas. My application has a fixed width, and setting the width of html2canvas to the width of my application, scaled the PDF to fit on an A4 paper.
html2canvas: { width: element_width},
Try adding the above option to see if it works. Try to find out the width of your print area in pixels and replace element_width with that width.
For completeness: I am using Plotly Dash to create web user interfaces. On my interface I include a button that when clicked generates a PDF report of my dashboard. Below I added the code that I used for this, in case anybody is looking for a Dash solution. To get this working in Dash, download html2pdf.bundlemin.js and copy it to the assets/ folder. The PDF file will be downloaded to the browsers default downloads folder (it might give a download prompt, however that wasn't how it worked for me).
from dash import html, clientside_callback
import dash_bootstrap_components as dbc
# Define your Dash app in the regular way
# In the layout define a component that will trigger the download of the
# PDF report. In this example a button will be responsible.
app.layout = html.Div(
id='main_container',
children = [
dbc.Button(
id='button_download_report',
children='Download PDF report',
className='me-1')
])
# Clientside callbacks allow you to directly insert Javascript code in your
# dashboards. There are also other ways, like including your own js files
# in the assets/ directory.
clientside_callback(
'''
function (button_clicked) {
if (button_clicked > 0) {
// Get the element that you want to print. In this example the
// whole dashboard is printed
var element = document.getElementById("main_container")
// create a date-time string to use for the filename
const d = new Date();
var month = (d.getMonth() + 1).toString()
if (month.length == 1) {
month = "0" + month
}
let text = d.getFullYear().toString() + month + d.getDay() + '-' + d.getHours() + d.getMinutes();
// Set the options to be used when printing the PDF
var main_container_width = element.style.width;
var opt = {
margin: 10,
filename: text + '_my-dashboard.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 3, width: main_container_width, dpi: 300 },
jsPDF: { unit: 'mm', format: 'A4', orientation: 'p' },
// Set pagebreaks if you like. It didn't work out well for me.
// pagebreak: { mode: ['avoid-all'] }
};
// Execute the save command.
html2pdf().from(element).set(opt).save();
}
}
''',
Output(component_id='button_download_report', component_property='n_clicks'),
Input(component_id='button_download_report', component_property='n_clicks')
)

How to automatically change the width of a preview edge according to the zoom level in cytoscape.js-edgehandles?

I am using cytoscape.js and the edgehandles extension (https://github.com/cytoscape/cytoscape.js-edgehandles).
Currently I am automatically resizing the line width of my edge using the event 'zoom':
cy.on('zoom', function (evt) {
let zoomFactor = cy.zoom()
let newFontSize = defaut_font_size.general * 1 / zoomFactor;
cy.filter("node[type='main-WP'][parent !^= 'compound']").style({
"font-size": newFontSize,
});
cy.edges().style({
'width': (0.3 * newFontSize),
});
});
How can I achieve something similar on the classes (.eh-preview, .eh-ghost-edge)?
You can give a function for a style. The drawback is the functions are not JSON serializable. So If you need to import/export a graph with cy.json() you should also apply the function styles.
cy.style().selector('edge.eh-preview')
.style({
'width': () => {
let zoomFactor = cy.zoom()
let newFontSize = defaut_font_size.general * 1 / zoomFactor;
return (0.3 * newFontSize);
},
}).update();

Forge Data Visualization not working on Revit rooms [ITA]

I followed the tutorials from the Forge Data Visualization extension documentation: https://forge.autodesk.com/en/docs/dataviz/v1/developers_guide/quickstart/ on a Revit file. I used the generateMasterViews option to translate the model and I can see the Rooms on the viewer, however I have problems coloring the surfaces of the floors: it seems that the ModelStructureInfo has no rooms.
The result of the ModelStructureInfo on the viewer.model is:
t {model: d, rooms: null}
Here is my code, I added the ITA localized versions of Rooms as 3rd parameter ("Locali"):
const dataVizExtn = await this.viewer.loadExtension("Autodesk.DataVisualization");
// Model Structure Info
let viewerDocument = this.viewer.model.getDocumentNode().getDocument();
const aecModelData = await viewerDocument.downloadAecModelData();
let levelsExt;
if (aecModelData) {
levelsExt = await viewer.loadExtension("Autodesk.AEC.LevelsExtension", {
doNotCreateUI: true
});
}
// get FloorInfo
const floorData = levelsExt.floorSelector.floorData;
const floor = floorData[2];
levelsExt.floorSelector.selectFloor(floor.index, true);
const model = this.viewer.model;
const structureInfo = new Autodesk.DataVisualization.Core.ModelStructureInfo(model);
let levelRoomsMap = await structureInfo.getLevelRoomsMap();
let rooms = levelRoomsMap.getRoomsOnLevel("2 - P2", false);
// Generates `SurfaceShadingData` after assigning each device to a room (Rooms--> Locali).
const shadingData = await structureInfo.generateSurfaceShadingData(devices, undefined, "Locali");
// Use the resulting shading data to generate heatmap from.
await dataVizExtn.setupSurfaceShading(model, shadingData, {
type: "PlanarHeatmap",
placePosition: "min",
usingSlicing: true,
});
// Register a few color stops for sensor values in range [0.0, 1.0]
const sensorType = "Temperature";
const sensorColors = [0x0000ff, 0x00ff00, 0xffff00, 0xff0000];
dataVizExtn.registerSurfaceShadingColors(sensorType, sensorColors);
// Function that provides a [0,1] value for the planar heatmap
function getSensorValue(surfaceShadingPoint, sensorType, pointData) {
const { x, y } = pointData;
const sensorValue = computeSensorValue(x, y);
return clamp(sensorValue, 0.0, 1.0);
}
const sensorType = "Temperature";
dataVizExtn.renderSurfaceShading(floor.name, sensorType, getSensorValue);
How can I solve this issue? Is there something else to do when using a different localization?
Here is a snapshot of what I get from the console:
Which viewer version you're using? There was an issue causing ModelStructureInfo cannot produce the correct LevelRoomsMap, but it gets fixed now. Please use v7.43.0 and try again. Here is the snapshot of my test:
BTW, if you see t {model: d, rooms: null} while constructing the ModelStructureInfo, it's alright, since the room data will be produced after you called ModelStructureInfo#getLevelRoomsMap or ModelStructureInfo#getRoomList.

Load Local Geojson Data with Leaflet and Vue.js

I am displaying in my Vue.js a leaflet Map with Geojson Data.
var map = L.map('map').setView([13.82, 106.62], 5);
// load a tile layer
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
attribution: 'Tiles by MAPC, Data by MassGIS',
maxZoom: 17,
minZoom: 6
}).addTo(map);
// The rodents json data need to be replace by a proper HTTPS not local Server
map.setZoom(4);
$.getJSON("C:\Users\simon\Desktop\FE\data\heat.geojson",function(data){
var locations = data.features.map(function(gps) {
// the heatmap plugin wants an array of each location
var location = gps.geometry.coordinates.reverse();
location.push(1.0);
return location; // e.g. [50.5, 30.5, 0.2], // lat, lng, intensity
});
var heat = L.heatLayer(locations, { radius: 25, maxOpacity: .8, scaleRadius: false, valueField: 'total',
// gradient: {
// 0.0: 'green',
// 0.5: 'yellow',
// 1.0: 'red'
// }
});
map.addLayer(heat);
});
On a local browser, I will simply load the data from the file location and it will work. How can I load the data in my local vue.js application?\
If i Change path to : HTTP404: NOT FOUND - The server has not found anything matching the requested URI (Uniform Resource Identifier).
(XHR)GET - http://localhost:8082/pages/..FE/data/sales_vietnam_heat.geojson

Adding extra information above the node in cytoscape.js

Using cytoscape.js to draw a graph. I need to add circle above the node at top-right position. After drawing the graph, is there any API wherein we can get positions of nodes.
Also, I have used the code as follows:
elements : {
nodes : [ {
data : {
id : '1',
name : 'A'
}
}
]
}
var pos = cy.nodes("#1").position();
'#1' is the ID of the node in the data attribute. However, we are not able to add the node/circle at that exact position.
If you want to get something like:
then this code adds the circle to a node knowing it's id:
function addCircle(nodeId, circleText){
var parentNode = cy.$('#' + nodeId);
if (parentNode.data('isCircle') || parentNode.data('circleId'))
return;
parentNode.lock();
var px = parentNode.position('x') + 10;
var py = parentNode.position('y') - 10;
var circleId = (cy.nodes().size() + 1).toString();
parentNode.data('circleId', circleId);
cy.add({
group: 'nodes',
data: { weight: 75, id: circleId, name: circleText, isCircle: true },
position: { x: px, y: py },
locked: true
}).css({
'background-color': 'yellow',
'shape': 'ellipse',
'background-opacity': 0.5
}).unselectify();
}
// ...
addCircle('1', 'Bubble A');
but it must be called after the node's positions are known, when the layout settles down.
The locking is there to prevent that node and it's circle get apart.
jsFiddle demo: http://jsfiddle.net/xmojmr/wvznb9pf/
Using compound node which would keep the node and it's circle together would be probably better, once the support for positioning child nodes is implemented.
Disclaimer: I'm cytoscape.js newbie, use at your own risk