Exporting HTML table to PDF with many columns using jsPDF - html

I'd like to link this question that doesn't have any answers. Exporting HTML table to PDF with its format with jsPDF. I'm having the same problem with him and the tables looks exactly alike. I have a 20 column html table and I want them to be exported to pdf without any problem. I'm using jsPDF for exporting the table. I have tried the html <colgroup> tag for the column width of my table and it didn't work out. I have the first 8 columns showing and 12 columns hidden. I want all of them to be exported to pdf.
I'd like to try this code but I didn't know how I will execute it using my button in my html.
$(document).on("click", "#btnExportToPDF", function () {
var table1 =
tableToJson($('#table1').get(0)),
cellWidth = 35,
rowCount = 0,
cellContents,
leftMargin = 2,
topMargin = 12,
topMarginTable = 55,
headerRowHeight = 13,
rowHeight = 9,
l = {
orientation: 'l',
unit: 'mm',
format: 'a3',
compress: true,
fontSize: 8,
lineHeight: 1,
autoSize: false,
printHeaders: true
};
var doc = new jsPDF(l, '', '', '');
doc.setProperties({
title: 'Test PDF Document',
subject: 'This is the subject',
author: 'author',
keywords: 'generated, javascript, web 2.0, ajax',
creator: 'author'
});
doc.cellInitialize();
$.each(table1, function (i, row)
{
rowCount++;
$.each(row, function (j, cellContent) {
if (rowCount == 1) {
doc.margins = 1;
doc.setFont("helvetica");
doc.setFontType("bold");
doc.setFontSize(9);
doc.cell(leftMargin, topMargin, cellWidth, headerRowHeight, cellContent, i)
}
else if (rowCount == 2) {
doc.margins = 1;
doc.setFont("times ");
doc.setFontType("italic"); // or for normal font type use ------ doc.setFontType("normal");
doc.setFontSize(8);
doc.cell(leftMargin, topMargin, cellWidth, rowHeight, cellContent, i);
}
else {
doc.margins = 1;
doc.setFont("courier ");
doc.setFontType("bolditalic ");
doc.setFontSize(6.5);
doc.cell(leftMargin, topMargin, cellWidth, rowHeight, cellContent, i); // 1st=left margin 2nd parameter=top margin, 3rd=row cell width 4th=Row height
}
})
})
doc.save('sample Report.pdf'); })
function tableToJson(table) {
var data = [];
// first row needs to be headers
var headers = [];
for (var i=0; i<table.rows[0].cells.length; i++) {
headers[i] = table.rows[0].cells[i].innerHTML.toLowerCase().replace(/ /gi,'');
}
// go through cells
for (var i=1; i<table.rows.length; i++) {
var tableRow = table.rows[i];
var rowData = {};
for (var j=0; j<tableRow.cells.length; j++) {
rowData[ headers[j] ] = tableRow.cells[j].innerHTML;
}
data.push(rowData);
}
return data; }
This is my code btw,
function demoFromHTML() {
$(document).find('tfoot').remove();
$('#table td:nth-child(8)').remove();
var pdf = new jsPDF('p', 'pt', 'letter', true);
// source can be HTML-formatted string, or a reference
// to an actual DOM element from which the text will be scraped.
source = $('#table')[0];
// we support special element handlers. Register them with jQuery-style
// ID selector for either ID or node name. ("#iAmID", "div", "span" etc.)
// There is no support for any other type of selectors
// (class, of compound) at this time.
specialElementHandlers = {
// element with id of "bypass" - jQuery style selector
'#bypassme': function (element, renderer) {
// true = "handled elsewhere, bypass text extraction"
return true
}
};
margins = {
top: 80,
bottom: 60,
left: 55,
width: 522
};
// all coords and widths are in jsPDF instance's declared units
// 'inches' in this case
pdf.fromHTML(
source, // HTML string or DOM elem ref.
margins.left, // x coord
margins.top, { // y coord
'width': margins.width, // max width of content on PDF
'elementHandlers': specialElementHandlers
},
function (dispose) {
// dispose: object with X, Y of the last line add to the PDF
// this allow the insertion of new lines after html
var name = document.getElementById("name").innerHTML;
pdf.save(name);
}, margins);
setTimeout("window.location.reload()",0.0000001);
}
With this code btw, $(document).find('tfoot').remove(); $('#table td:nth-child(8)').remove(); I remove my footer and the 8th column of my table.

Related

simplest way to update data in VegaEmbed

I made a small graph to show some data from a bluetooth device.
I used a sample I found for VegaEmbed, it was all very easy.
But the sample uses a timer to get data, so even if there is no data the dataset will be changed. What is the simples way to update data inside VegaEmbed from another part of the website ?
I cannot call res.view.change('table', changeSet).run(); from outside VegaEmbded..
Here is snappshot of the code :
(the function handleDataChanged is called when there is bluetooth data.)
function handleDataChanged(event) {
var value = event.target.value;
value = value.buffer ? value : new DataView(value);
let result = {};
let index = 1;
datapointx = value.getInt16(index, /*littleEndian=*/false);
console.log('X: ' + value.getInt16(index, /*littleEndian=*/false));
index += 2;
datapointy = value.getInt16(index, /*littleEndian=*/true);
console.log('Y: ' + value.getInt16(index, /*littleEndian=*/false));
index += 2;
datapointz = value.getInt16(index, /*littleEndian=*/true);
console.log('Z: ' + value.getInt16(index, /*littleEndian=*/false));
index += 2;
}
</script>
<script>
document.querySelector('button').addEventListener('click', function() {
onButtonClick();
});
</script>
<script type="text/javascript">
var vlSpec = {
$schema: 'https://vega.github.io/schema/vega-lite/v3.json',
data: {name: 'table'},
width: 400,
mark: 'line',
encoding: {
x: {field: 'x', type: 'quantitative', scale: {zero: false}},
y: {field: 'y', type: 'quantitative'},
color: {field: 'category', type: 'nominal'}
}
};
vegaEmbed('#chart', vlSpec).then(function(res) {
/**
* Generates a new tuple with random walk.
*/
function newGenerator() {
var counter = -1;
var previousY = [5, 5, 5];
return function() {
counter++;
var newVals = previousY.map(function(v, c)
{
console.log('c = ' + c);
var yval = 0;
if (c == 0)
yval = datapointx;
if (c == 1)
yval = datapointy;
if (c == 2)
yval = datapointz;
return {
x: counter,
// y: v + Math.round(Math.random() * 10 - c * 3),
y: yval,
category: c
};
});
previousY = newVals.map(function(v) {
return v.y;
});
return newVals;
};
}
var valueGenerator = newGenerator();
var minimumX = -100;
window.setInterval(function() {
minimumX++;
var changeSet = vega
.changeset()
.insert(valueGenerator())
.remove(function(t) {
return t.x < minimumX;
});
res.view.change('table', changeSet).run();
}, 100);
});
</script>
The simplest way to update data in an existing vega-lite chart is to use a streaming data model. There is an example in the Vega-Lite documentation here: https://vega.github.io/vega-lite/tutorials/streaming.html

Chart.js dynamic bar width

I have a requirement to render a set of time series data of contiguous blocks.
I need to describe a series of bars which could span many hours, or just minutes, with their own Y value.
I'm not sure if ChartJS is what I should be using for this, but I have looked at extending the Bar type, but it seems very hard coded for each bar to be the same width. The Scale Class internally is used for labels, chart width etc, not just the bars themselves.
I am trying to achieve something like this that works in Excel: http://peltiertech.com/variable-width-column-charts/
Has anyone else had to come up with something similar?
I found I needed to do this and the answer by #potatopeelings was great, but out of date for version 2 of Chartjs. I did something similar by creating my own controller/chart type via extending bar:
//controller.barw.js
module.exports = function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.barw = {
hover: {
mode: 'label'
},
scales: {
xAxes: [{
type: 'category',
// Specific to Bar Controller
categoryPercentage: 0.8,
barPercentage: 0.9,
// grid line settings
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
type: 'linear'
}]
}
};
Chart.controllers.barw = Chart.controllers.bar.extend({
/**
* #private
*/
getRuler: function() {
var me = this;
var scale = me.getIndexScale();
var options = scale.options;
var stackCount = me.getStackCount();
var fullSize = scale.isHorizontal()? scale.width : scale.height;
var tickSize = fullSize / scale.ticks.length;
var categorySize = tickSize * options.categoryPercentage;
var fullBarSize = categorySize / stackCount;
var barSize = fullBarSize * options.barPercentage;
barSize = Math.min(
helpers.getValueOrDefault(options.barThickness, barSize),
helpers.getValueOrDefault(options.maxBarThickness, Infinity));
return {
fullSize: fullSize,
stackCount: stackCount,
tickSize: tickSize,
categorySize: categorySize,
categorySpacing: tickSize - categorySize,
fullBarSize: fullBarSize,
barSize: barSize,
barSpacing: fullBarSize - barSize,
scale: scale
};
},
/**
* #private
*/
calculateBarIndexPixels: function(datasetIndex, index, ruler) {
var me = this;
var scale = ruler.scale;
var options = scale.options;
var isCombo = me.chart.isCombo;
var stackIndex = me.getStackIndex(datasetIndex);
var base = scale.getPixelForValue(null, index, datasetIndex, isCombo);
var size = ruler.barSize;
var dataset = me.chart.data.datasets[datasetIndex];
if(dataset.weights) {
var total = dataset.weights.reduce((m, x) => m + x, 0);
var perc = dataset.weights[index] / total;
var offset = 0;
for(var i = 0; i < index; i++) {
offset += dataset.weights[i] / total;
}
var pixelOffset = Math.round(ruler.fullSize * offset);
var base = scale.isHorizontal() ? scale.left : scale.top;
base += pixelOffset;
size = Math.round(ruler.fullSize * perc);
size -= ruler.categorySpacing;
size -= ruler.barSpacing;
}
base -= isCombo? ruler.tickSize / 2 : 0;
base += ruler.fullBarSize * stackIndex;
base += ruler.categorySpacing / 2;
base += ruler.barSpacing / 2;
return {
size: size,
base: base,
head: base + size,
center: base + size / 2
};
},
});
};
Then you need to add it to your chartjs instance like this:
import Chart from 'chart.js'
import barw from 'controller.barw'
barw(Chart); //add plugin to chartjs
and finally, similar to the other answer, the weights of the bar widths need to be added to the data set:
var data = {
labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.7)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 30, 56, 65, 40],
weights: [1, 0.9, 1, 2, 1, 4, 0.3]
},
]
};
This will hopefully get someone onto the right track. What I have certainly isn't perfect, but if you make sure you have the right number of weight to data points, you should be right.
Best of luck.
This is based on the #Shane's code, I just posted to help, since is a common question.
calculateBarIndexPixels: function (datasetIndex, index, ruler) {
const options = ruler.scale.options;
const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options) : computeFitCategoryTraits(index, ruler, options);
const barSize = range.chunk;
const stackIndex = this.getStackIndex(datasetIndex, this.getMeta().stack);
let center = range.start + range.chunk * stackIndex + range.chunk / 2;
let size = range.chunk * range.ratio;
let start = range.start;
const dataset = this.chart.data.datasets[datasetIndex];
if (dataset.weights) {
//the max weight should be one
size = barSize * dataset.weights[index];
const meta = this.chart.controller.getDatasetMeta(0);
const lastModel = index > 0 ? meta.data[index - 1]._model : null;
//last column takes the full bar
if (lastModel) {
//start could be last center plus half of last column width
start = lastModel.x + lastModel.width / 2;
}
center = start + size * stackIndex + size / 2;
}
return {
size: size,
base: center - size / 2,
head: center + size / 2,
center: center
};
}
For Chart.js you can create a new extension based on the bar class to do this. It's a bit involved though - however most of it is a copy paste of the bar type library code
Chart.types.Bar.extend({
name: "BarAlt",
// all blocks that don't have a comment are a direct copy paste of the Chart.js library code
initialize: function (data) {
// the sum of all widths
var widthSum = data.datasets[0].data2.reduce(function (a, b) { return a + b }, 0);
// cumulative sum of all preceding widths
var cumulativeSum = [ 0 ];
data.datasets[0].data2.forEach(function (e, i, arr) {
cumulativeSum.push(cumulativeSum[i] + e);
})
var options = this.options;
// completely rewrite this class to calculate the x position and bar width's based on data2
this.ScaleClass = Chart.Scale.extend({
offsetGridLines: true,
calculateBarX: function (barIndex) {
var xSpan = this.width - this.xScalePaddingLeft;
var x = this.xScalePaddingLeft + (cumulativeSum[barIndex] / widthSum * xSpan) - this.calculateBarWidth(barIndex) / 2;
return x + this.calculateBarWidth(barIndex);
},
calculateBarWidth: function (index) {
var xSpan = this.width - this.xScalePaddingLeft;
return (xSpan * data.datasets[0].data2[index] / widthSum);
}
});
this.datasets = [];
if (this.options.showTooltips) {
Chart.helpers.bindEvents(this, this.options.tooltipEvents, function (evt) {
var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
this.eachBars(function (bar) {
bar.restore(['fillColor', 'strokeColor']);
});
Chart.helpers.each(activeBars, function (activeBar) {
activeBar.fillColor = activeBar.highlightFill;
activeBar.strokeColor = activeBar.highlightStroke;
});
this.showTooltip(activeBars);
});
}
this.BarClass = Chart.Rectangle.extend({
strokeWidth: this.options.barStrokeWidth,
showStroke: this.options.barShowStroke,
ctx: this.chart.ctx
});
Chart.helpers.each(data.datasets, function (dataset, datasetIndex) {
var datasetObject = {
label: dataset.label || null,
fillColor: dataset.fillColor,
strokeColor: dataset.strokeColor,
bars: []
};
this.datasets.push(datasetObject);
Chart.helpers.each(dataset.data, function (dataPoint, index) {
datasetObject.bars.push(new this.BarClass({
value: dataPoint,
label: data.labels[index],
datasetLabel: dataset.label,
strokeColor: dataset.strokeColor,
fillColor: dataset.fillColor,
highlightFill: dataset.highlightFill || dataset.fillColor,
highlightStroke: dataset.highlightStroke || dataset.strokeColor
}));
}, this);
}, this);
this.buildScale(data.labels);
// remove the labels - they won't be positioned correctly anyway
this.scale.xLabels.forEach(function (e, i, arr) {
arr[i] = '';
})
this.BarClass.prototype.base = this.scale.endPoint;
this.eachBars(function (bar, index, datasetIndex) {
// change the way the x and width functions are called
Chart.helpers.extend(bar, {
width: this.scale.calculateBarWidth(index),
x: this.scale.calculateBarX(index),
y: this.scale.endPoint
});
bar.save();
}, this);
this.render();
},
draw: function (ease) {
var easingDecimal = ease || 1;
this.clear();
var ctx = this.chart.ctx;
this.scale.draw(1);
Chart.helpers.each(this.datasets, function (dataset, datasetIndex) {
Chart.helpers.each(dataset.bars, function (bar, index) {
if (bar.hasValue()) {
bar.base = this.scale.endPoint;
// change the way the x and width functions are called
bar.transition({
x: this.scale.calculateBarX(index),
y: this.scale.calculateY(bar.value),
width: this.scale.calculateBarWidth(index)
}, easingDecimal).draw();
}
}, this);
}, this);
}
});
You pass in the widths like below
var data = {
labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,0.8)",
highlightFill: "rgba(220,220,220,0.7)",
highlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 30, 56, 65, 40],
data2: [10, 20, 30, 20, 10, 40, 10]
},
]
};
and you call it like so
var ctx = document.getElementById('canvas').getContext('2d');
var myLineChart = new Chart(ctx).BarAlt(data);
Fiddle - http://jsfiddle.net/moye0cp4/

Passing a JSON AngularJS structure to a mongoDB database

I have just recentely used AngularJS to "convert" a data structure I had in pure SVG format into JSON format.
Now, I want to store such a structure in a MongoDB database to start finally connecting some components of the MEAN stack together and start seeing some things working! Basically, I have the following code inside a Webstorm AngularJS project:
JS:
var app = angular.module('app', []);
var RectangleDim=30;
app.controller('MainCtrl', function($scope) {
$scope.graph = {'width': 5000, 'height': 5000};
$scope.circles = [
/* JSON.parse("{\"x\": 85, \"y\": 20, \"r\":15}"),
{"x": 20, "y": 60, "r":20},
{"x": 18, "y": 10, "r":40} */
];
$scope.draw=function(val)
{
// val = document.getElementById("NumQuest").value;
return JSON.parse('{\"cx\":'+val+', "cy": 20, "r":30}');
// $scope.circles.push(JSON.parse('{\"x\":'+val+', "y": 220, "r":30}'));
};
$scope.rectangles = [
// {'x':220, 'y':220, 'width' : 300, 'height' : 100},
// {'x':520, 'y':220, 'width' : 10, 'height' : 10},
];
$scope.DrawRect=function(xpos,ypos) {
return JSON.parse('{\"x\":' + xpos + ', \"y\":' + ypos + ', \"width\":' + RectangleDim + ', \"height\":' + RectangleDim+ '}');
};
$scope.Debug=function(desiredNo){
desiredNo=document.getElementById("NumQuest").value;
for(var i = 0;i < RectangleDim*desiredNo+desiredNo;i++){
$scope.rectangles.push($scope.DrawRect(i+RectangleDim+1,40));
}
};
$scope.DrawLineOdd=function(desiredNo,lineNo,pozY){
var pozX = lineNo*RectangleDim;
var aux = 2*Math.floor(Math.sqrt(desiredNo))-1-2*lineNo;
for (var j = 0; j < aux; j++) {
$scope.rectangles.push($scope.DrawRect(pozX, pozY));//$scope.DrawRect(pozX, pozY);
pozX += RectangleDim;
}
//return aux;
};
$scope.DrawMatrixPerfectProgression=function(desiredNo) {
desiredNo=document.getElementById("NumQuest").value;
var line=0;
var pozy=0;
while(line<Math.floor(Math.sqrt(desiredNo))) {
$scope.DrawLineOdd(desiredNo, line, pozy);
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
};
$scope.DrawLineEven=function(desiredNo, lineNo, pozY){
var pozX = lineNo*RectangleDim;
//var pozY = lineno*20;
var aux = 2*Math.floor(Math.sqrt(desiredNo))-2*lineNo;
for (var j = 0; j < aux; j++) {
$scope.rectangles.push($scope.DrawRect(pozX, pozY));
pozX += RectangleDim;
}
//return aux;
};
$scope.DrawMatrixEvenProgression=function(desiredNo) {
desiredNo=document.getElementById("NumQuest").value;
var line=0;
var pozy=0;
while(line<Math.floor((Math.sqrt(4*desiredNo+1)-1)/2)) {
$scope.DrawLineEven(desiredNo, line, pozy);
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
};
$scope.AddExtraRectangles=function(desiredNo) {
desiredNo = document.getElementById("NumQuest").value;
var arg1 = desiredNo - ( Math.floor(Math.sqrt(desiredNo))*Math.floor(Math.sqrt(desiredNo)));
var arg2 = desiredNo-(Math.floor((Math.sqrt(4*desiredNo+1)-1)/2)*Math.floor((Math.sqrt(4*desiredNo+1)-1)/2))-Math.floor((Math.sqrt(4*desiredNo+1)-1)/2);
var OptimalLeftOver = Math.min( arg1 ,arg2 );
//We add two rectangles per row: one at the beginning one at the end
//we start with the row below the first one
var line;
var pozy;
var pozx1, pozx2;
var nRectLine_i;
if(OptimalLeftOver===arg1){
line=1;//1st line is skipped
pozy=RectangleDim;
pozx1 = 0;
while(OptimalLeftOver>0) {
nRectLine_i = 2* Math.floor(Math.sqrt(desiredNo))-1-2*line;
pozx2 = (line-1)*RectangleDim+RectangleDim*(nRectLine_i+1);//pozx1+nRectLine_i+2*RectangleDim;
$scope.rectangles.push($scope.DrawRect(pozx1,pozy));
OptimalLeftOver-=1;
if(OptimalLeftOver>0) {
$scope.rectangles.push($scope.DrawRect(pozx2, pozy));
OptimalLeftOver -= 1;
}
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
pozx1=RectangleDim*line - RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
}
else {
line=1;//1st line is skipped
pozy=RectangleDim;
pozx1 = 0;
while(OptimalLeftOver>0) {
nRectLine_i = 2* Math.floor(Math.sqrt(desiredNo))-2*line;
pozx2 = RectangleDim*(line-1)+RectangleDim*(nRectLine_i+1);//pozx1+nRectLine_i+2*RectangleDim;
$scope.rectangles.push($scope.DrawRect(pozx1,pozy));
OptimalLeftOver-=1;
if(OptimalLeftOver>0) {
$scope.rectangles.push($scope.DrawRect(pozx2, pozy));
OptimalLeftOver -= 1;
}
//document.getElementById("val").innerHTML = teste;
line += 1;
pozy+=RectangleDim;
pozx1=RectangleDim*line - RectangleDim;
}
//document.getElementById('tablePrint').innerHTML = finalTable;
}
};
/* $scope.DrawMatrix=function(desiredNo)
{
/* Chooses optimal leftover number based on the progression formulas.
Attempts to minimize the work of the designer of the response form without
making too much assumptions
desiredNo = document.getElementById("NumQuest").value;
var arg1 = desiredNo - ( Math.floor(Math.sqrt(desiredNo))*Math.floor(Math.sqrt(desiredNo)));
var arg2 = desiredNo - (Math.floor((Math.sqrt(4*desiredNo+1)-1)/2)*Math.floor((Math.sqrt(4*desiredNo+1)-1)/2))-Math.floor((Math.sqrt(4*desiredNo+1)-1)/2);
var OptimalLeftOver = Math.min( arg1 ,arg2 );
document.getElementById("val").innerHTML = 'There are '+OptimalLeftOver+' questions missing!'+ arg1+ '___'+arg2;
console.log(arg1);
if(OptimalLeftOver===arg1){
DrawMatrixPerfectProgression(desiredNo);
AddExtraRectangles(desiredNo);
}
else {
DrawMatrixEvenProgression(desiredNo);
AddExtraRectangles(desiredNo);
}
}; */
}
);
angular.bootstrap(document.getElementById('body'), ["app"]);
The relevant part of the code is the $scope.rectangles array which contains the JSON.parse of the strings representing my data structure on the html side and that structure in JSON (or JSON parsed or whatever) is what I want to save in the MongoDB database...How can I do that? The HTML relevant part is just like this:
HTML:
<p><button ng-click="DrawMatrixEvenProgression(NumQuest)">Draw</button></p>
<svg ng-attr-height="{{graph.height}}" ng-attr-width="{{graph.width}}">
<rect ng-repeat="rect in rectangles"
ng-attr-x="{{rect.x}}"
ng-attr-y="{{rect.y}}"
ng-attr-width="{{rect.width}}"
ng-attr-height="{{rect.height}}">
</rect>
</svg>
Any help will be appreciated... Can I start by adding more files to that project to handle the database and then things will be linked together?
Like adding stuff to handle the mongoose and the connections?
Thanks in advance!
Because Angular is a front-end framework. So to communicate with database (in this case MongoDB) you need to have application on the server-side to handle this and I suggest you to use Node.js and Mongoose as a MongoDB driver.
Node.js
Mongoose
Come back to Angular, you can create Angular service or factory and let the them talk to your server with service like $http or $resource.
Angular service documentation
Example for angular service
angular.module('app')
.factory('RectangleService', function($http){
return {
create: create
}
function create(rectangle){
// make http request to the server
return $http({
url: 'API_URL',
method: 'GET',
params: rectangle
});
}
});
After you create your service you have to inject it to your controller and
you may create some function to your $scope to talk with service like this
app.controller('MainCtrl', function($scope, RectangleService) { // <-- Inject service to controller
// your controller code
$scope.createRectangle = function(rectangle){
RectangleService.create(rectangle);
}
});
You can map createRectangle function to directive like ng-click and pass your json data as a parameter
Because I don't know what server-side language you can use, so I don't come with an example for Node.js & Mongoose
Hope this can help :)

How to move 3D model on Cesium

I wanted to move the model dynamically using keyboard shortcuts. I could not find relevant article on that.
So for now, I'm trying to move the model on click. When click on the model. The model has to move in one direction (increment the value 1 on tick). Find below the sandcastle code for that.
var selectedMesh; var i=0;
var viewer = new Cesium.Viewer('cesiumContainer', {
infoBox: false,
selectionIndicator: false
});
var handle = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
function createModel(url, height) {
viewer.entities.removeAll();
var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);
var heading = Cesium.Math.toRadians(135);
var pitch = 0;
var roll = 0;
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);
var entity = viewer.entities.add({
name: url,
position: position,
orientation: orientation,
model: {
uri: url,
minimumPixelSize: 128
}
});
viewer.trackedEntity = entity;
viewer.clock.onTick.addEventListener(function () {
if (selectedMesh) {
console.log("Before 0 : " + selectedMesh.primitive.modelMatrix[12]);
selectedMesh.primitive.modelMatrix[12] = selectedMesh.primitive.modelMatrix[12] + 1;
console.log("After 0 : " + selectedMesh.primitive.modelMatrix[12]);
}
});
}
handle.setInputAction(function (movement) {
console.log("LEFT CLICK");
var pick = viewer.scene.pick(movement.position);
if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) {
if (!selectedMesh) {
selectedMesh = pick;
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
var options = [{
text: 'Aircraft',
onselect: function () {
createModel('../../SampleData/models/CesiumAir/Cesium_Air.bgltf', 5000.0);
}
}, {
text: 'Ground vehicle',
onselect: function () {
createModel('../../SampleData/models/CesiumGround/Cesium_Ground.bgltf', 0);
}
}, {
text: 'Milk truck',
onselect: function () {
createModel('../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck.bgltf', 0);
}
}, {
text: 'Skinned character',
onselect: function () {
createModel('../../SampleData/models/CesiumMan/Cesium_Man.bgltf', 0);
}
}];
Sandcastle.addToolbarMenu(options);
When I click, the model is moving for the first time. After that, It stays on the same place. I've printed the value in the console. It seems the value is not changing. I'm not sure about the problem here. or I'm implementing the transformation wrongly.
If you keep track of the current lat and lon of the entity, and adjust that lat and lon based on user input, all you need to do is update the orientation of the entity.
var lon = // the updated lon
var lat = // updated lat
var position = Cesium.Cartesian3.fromDegrees(lon, lat, height);
var heading = Cesium.Math.toRadians(135);
var pitch = 0;
var roll = 0;
// create an orientation based on the new position
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);
Then you just need to update the orientation of the entity.
entity.orientation = orientation;
By changing the value, the models orientation, and therefore position will get updated.

How to include dependencies in google analytics

I am trying to create and show custom dashboard of google analytics in my own website. I am following tuts on ga-dev-tools to achieve this. Problem is i am unable to add components and dependencies including view-selector2.js, date-range-selector.js and active-users.js. I am unable to find these files anywhere on google as well. when i paste the code given on third party visualizations it says view-selector2.js, date-range-selector.js and active-users.js files not found in console.
Here is my code so far
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<title>Custom Components Google Analytics</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<script>
(function (w, d, s, g, js, fs) {
g = w.gapi || (w.gapi = {});
g.analytics = {q: [], ready: function (f) {
this.q.push(f);
}};
js = d.createElement(s);
fs = d.getElementsByTagName(s)[0];
js.src = 'https://apis.google.com/js/platform.js';
fs.parentNode.insertBefore(js, fs);
js.onload = function () {
g.load('analytics');
};
}(window, document, 'script'));
</script>
<div id="embed-api-auth-container"></div>
<div id="view-selector-container"></div>
<div id="active-users-container"></div>
<div id="chart-1-container"></div>
<div id="legend-1-container"></div>
<div id="chart-2-container"></div>
<div id="legend-2-container"></div>
<div id="chart-3-container"></div>
<div id="legend-3-container"></div>
<div id="chart-4-container"></div>
<div id="legend-4-container"></div>
<!-- Include the ViewSelector2 component script. -->
<script src="/public/javascript/embed-api/view-selector2.js"></script>
<!-- Include the DateRangeSelector component script. -->
<script src="/public/javascript/embed-api/date-range-selector.js"></script>
<script>
gapi.analytics.ready(function () {
/**
* Authorize the user immediately if the user has already granted access.
* If no access has been created, render an authorize button inside the
* element with the ID "embed-api-auth-container".
*/
gapi.analytics.auth.authorize({
container: 'embed-api-auth-container',
clientid: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
});
/**
* Create a new ActiveUsers instance to be rendered inside of an
* element with the id "active-users-container" and poll for changes every
* five seconds.
*/
var activeUsers = new gapi.analytics.ext.ActiveUsers({
container: 'active-users-container',
pollingInterval: 5
});
/**
* Add CSS animation to visually show the when users come and go.
*/
activeUsers.once('success', function () {
var element = this.container.firstChild;
var timeout;
this.on('change', function (data) {
var element = this.container.firstChild;
var animationClass = data.delta > 0 ? 'is-increasing' : 'is-decreasing';
element.className += (' ' + animationClass);
clearTimeout(timeout);
timeout = setTimeout(function () {
element.className =
element.className.replace(/ is-(increasing|decreasing)/g, '');
}, 3000);
});
});
/**
* Create a new ViewSelector2 instance to be rendered inside of an
* element with the id "view-selector-container".
*/
var viewSelector = new gapi.analytics.ext.ViewSelector2({
container: 'view-selector-container',
})
.execute();
/**
* Update the activeUsers component, the Chartjs charts, and the dashboard
* title whenever the user changes the view.
*/
viewSelector.on('viewChange', function (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
// Start tracking active users for this view.
activeUsers.set(data).execute();
// Render all the of charts for this view.
renderWeekOverWeekChart(data.ids);
renderYearOverYearChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
});
/**
* Draw the a chart.js line chart with data from the specified view that
* overlays session data for the current week over session data for the
* previous week.
*/
function renderWeekOverWeekChart(ids) {
// Adjust `now` to experiment with different days, for testing only...
var now = moment(); // .subtract(3, 'day');
var thisWeek = query({
'ids': ids,
'dimensions': 'ga:date,ga:nthDay',
'metrics': 'ga:sessions',
'start-date': moment(now).subtract(1, 'day').day(0).format('YYYY-MM-DD'),
'end-date': moment(now).format('YYYY-MM-DD')
});
var lastWeek = query({
'ids': ids,
'dimensions': 'ga:date,ga:nthDay',
'metrics': 'ga:sessions',
'start-date': moment(now).subtract(1, 'day').day(0).subtract(1, 'week')
.format('YYYY-MM-DD'),
'end-date': moment(now).subtract(1, 'day').day(6).subtract(1, 'week')
.format('YYYY-MM-DD')
});
Promise.all([thisWeek, lastWeek]).then(function (results) {
var data1 = results[0].rows.map(function (row) {
return +row[2];
});
var data2 = results[1].rows.map(function (row) {
return +row[2];
});
var labels = results[1].rows.map(function (row) {
return +row[0];
});
labels = labels.map(function (label) {
return moment(label, 'YYYYMMDD').format('ddd');
});
var data = {
labels: labels,
datasets: [
{
label: 'Last Week',
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
data: data2
},
{
label: 'This Week',
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
data: data1
}
]
};
new Chart(makeCanvas('chart-1-container')).Line(data);
generateLegend('legend-1-container', data.datasets);
});
}
/**
* Draw the a chart.js bar chart with data from the specified view that
* overlays session data for the current year over session data for the
* previous year, grouped by month.
*/
function renderYearOverYearChart(ids) {
// Adjust `now` to experiment with different days, for testing only...
var now = moment(); // .subtract(3, 'day');
var thisYear = query({
'ids': ids,
'dimensions': 'ga:month,ga:nthMonth',
'metrics': 'ga:users',
'start-date': moment(now).date(1).month(0).format('YYYY-MM-DD'),
'end-date': moment(now).format('YYYY-MM-DD')
});
var lastYear = query({
'ids': ids,
'dimensions': 'ga:month,ga:nthMonth',
'metrics': 'ga:users',
'start-date': moment(now).subtract(1, 'year').date(1).month(0)
.format('YYYY-MM-DD'),
'end-date': moment(now).date(1).month(0).subtract(1, 'day')
.format('YYYY-MM-DD')
});
Promise.all([thisYear, lastYear]).then(function (results) {
var data1 = results[0].rows.map(function (row) {
return +row[2];
});
var data2 = results[1].rows.map(function (row) {
return +row[2];
});
var labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// Ensure the data arrays are at least as long as the labels array.
// Chart.js bar charts don't (yet) accept sparse datasets.
for (var i = 0, len = labels.length; i < len; i++) {
if (data1[i] === undefined)
data1[i] = null;
if (data2[i] === undefined)
data2[i] = null;
}
var data = {
labels: labels,
datasets: [
{
label: 'Last Year',
fillColor: "rgba(220,220,220,0.5)",
strokeColor: "rgba(220,220,220,1)",
data: data2
},
{
label: 'This Year',
fillColor: "rgba(151,187,205,0.5)",
strokeColor: "rgba(151,187,205,1)",
data: data1
}
]
};
new Chart(makeCanvas('chart-2-container')).Bar(data);
generateLegend('legend-2-container', data.datasets);
})
.catch(function (err) {
console.error(err.stack);
})
}
/**
* Draw the a chart.js doughnut chart with data from the specified view that
* show the top 5 browsers over the past seven days.
*/
function renderTopBrowsersChart(ids) {
query({
'ids': ids,
'dimensions': 'ga:browser',
'metrics': 'ga:pageviews',
'sort': '-ga:pageviews',
'max-results': 5
})
.then(function (response) {
var data = [];
var colors = ['#4D5360', '#949FB1', '#D4CCC5', '#E2EAE9', '#F7464A'];
response.rows.forEach(function (row, i) {
data.push({value: +row[1], color: colors[i], label: row[0]});
});
new Chart(makeCanvas('chart-3-container')).Doughnut(data);
generateLegend('legend-3-container', data);
});
}
/**
* Draw the a chart.js doughnut chart with data from the specified view that
* compares sessions from mobile, desktop, and tablet over the past seven
* days.
*/
function renderTopCountriesChart(ids) {
query({
'ids': ids,
'dimensions': 'ga:country',
'metrics': 'ga:sessions',
'sort': '-ga:sessions',
'max-results': 5
})
.then(function (response) {
var data = [];
var colors = ['#4D5360', '#949FB1', '#D4CCC5', '#E2EAE9', '#F7464A'];
response.rows.forEach(function (row, i) {
data.push({
label: row[0],
value: +row[1],
color: colors[i]
});
});
new Chart(makeCanvas('chart-4-container')).Doughnut(data);
generateLegend('legend-4-container', data);
});
}
/**
* Extend the Embed APIs `gapi.analytics.report.Data` component to
* return a promise the is fulfilled with the value returned by the API.
* #param {Object} params The request parameters.
* #return {Promise} A promise.
*/
function query(params) {
return new Promise(function (resolve, reject) {
var data = new gapi.analytics.report.Data({query: params});
data.once('success', function (response) {
resolve(response);
})
.once('error', function (response) {
reject(response);
})
.execute();
});
}
/**
* Create a new canvas inside the specified element. Set it to be the width
* and height of its container.
* #param {string} id The id attribute of the element to host the canvas.
* #return {RenderingContext} The 2D canvas context.
*/
function makeCanvas(id) {
var container = document.getElementById(id);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
container.innerHTML = '';
canvas.width = container.offsetWidth;
canvas.height = container.offsetHeight;
container.appendChild(canvas);
return ctx;
}
/**
* Create a visual legend inside the specified element based off of a
* Chart.js dataset.
* #param {string} id The id attribute of the element to host the legend.
* #param {Array.<Object>} items A list of labels and colors for the legend.
*/
function generateLegend(id, items) {
var legend = document.getElementById(id);
legend.innerHTML = items.map(function (item) {
var color = item.color || item.fillColor;
var label = item.label;
return '<li><i style="background:' + color + '"></i>' + label + '</li>';
}).join('');
}
// Set some global Chart.js defaults.
Chart.defaults.global.animationSteps = 60;
Chart.defaults.global.animationEasing = 'easeInOutQuart';
Chart.defaults.global.responsive = true;
Chart.defaults.global.maintainAspectRatio = false;
});
</script>
</body>
</html>
If you view the source for that page, you can see those sample components are located here:
https://ga-dev-tools.appspot.com/public/javascript/embed-api/view-selector2.js
https://ga-dev-tools.appspot.com/public/javascript/embed-api/date-range-selector.js
https://ga-dev-tools.appspot.com/public/javascript/embed-api/active-users.js
If you look at their Github repo, you can find them here:
https://github.com/googleanalytics/ga-dev-tools/tree/master/assets/javascript/embed-api
Note, if you're going to use these components on your own site, you should copy the source files and not link to them. appspot.com is not a CDN and your site's performance will suffer if you do.