I want to populate my json data into d3 charts. But how to get json data from controller?
Here rootVO is json file and i am passing to jsp but i don't know how to collect it and use it in jsp?
Controller class
#RequestMapping("/sunburst")
public String sunburstChart(Model model)
{
model.addAttribute("jsonData", rootVO);
return "sunburstChart";
}
Another jsp file from where i am calling that method
$.ajax
({
url: "sunburst",
async: false,
success: function(data)
{
console.log(data);
$("#sunburstChart").append(data);
}
});
Here is my sunburstChart.jspin which i want json data
<!DOCTYPE html>
<head>
<title>Sunburst Tutorial (d3 v4), Part 3</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<style>
#import url('https://fonts.googleapis.com/css?family=Raleway');
body {
font-family: "Raleway", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
<body>
<svg></svg>
<label><input class="sizeSelect" type="radio" name="mode" value="size" checked /> Size</label>
<label><input class="sizeSelect" type="radio" name="mode" value="count" /> Count</label>
</body>
<script>
// Variables
var width = 500;
var height = 500;
var radius = Math.min(width, height) / 2;
var color = d3.scaleOrdinal(d3.schemeCategory20b);
var sizeIndicator = "size";
var colorIndicator = "sentiment";
// Size our <svg> element, add a <g> element, and move translate 0,0 to the center of the element.
var g = d3.select('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
// Create our sunburst data structure and size it.
var partition = d3.partition()
.size([2 * Math.PI, radius]);
// Get the data from our JSON file
d3.json(
$.ajax
({
type:"GET",
dataType : 'json',
url : '/sunburst',
success : function(response)
{
},
error: function() {
alert("asda");
}
});
, function(error, nodeData) {
if (error) throw error;
// Find the root node, calculate the node.value, and sort our nodes by node.value
var root = d3.hierarchy(nodeData)
.sum(function (d) { return d.size; })
.sort(function(a, b) { return b.value - a.value; });
// Calculate the size of each arc; save the initial angles for tweening.
partition(root);
arc = d3.arc()
.startAngle(function (d) { d.x0s = d.x0; return d.x0; })
.endAngle(function (d) { d.x1s = d.x1; return d.x1; })
.innerRadius(function (d) { return d.y0; })
.outerRadius(function (d) { return d.y1; });
// Add a <g> element for each node; create the slice variable since we'll refer to this selection many times
var slice = g.selectAll('g')
.data(root.descendants())
.enter().append('g').attr("class", "node");
// Append <path> elements and draw lines based on the arc calculations. Last, color the lines and the slices.
slice.append('path').attr("display", function (d) { return d.depth ? null : "none"; })
.attr("d", arc)
.style('stroke', '#fff')
.style("fill", function (d) { return color((d.children ? d : d.parent).data.name); });
// Populate the <text> elements with our data-driven titles.
slice.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")rotate(" + computeTextRotation(d) + ")"; })
.attr("dx", "-20")
.attr("dy", ".5em")
.text(function(d) { return d.parent ? d.data.name : "" });
// Redraw the Sunburst Based on User Input
d3.selectAll(".sizeSelect").on("click", function(d,i) {
// Determine how to size the slices.
if (this.value === "size") {
root.sum(function (d) { return d.size; });
} else {
root.count();
}
partition(root);
slice.selectAll("path").transition().duration(750).attrTween("d", arcTweenPath);
slice.selectAll("text").transition().duration(750).attrTween("transform", arcTweenText);
});
});
/**
* When switching data: interpolate the arcs in data space.
* #param {Node} a
* #param {Number} i
* #return {Number}
*/
function arcTweenPath(a, i) {
var oi = d3.interpolate({ x0: a.x0s, x1: a.x1s }, a);
function tween(t) {
var b = oi(t);
a.x0s = b.x0;
a.x1s = b.x1;
return arc(b);
}
return tween;
}
/**
* When switching data: interpolate the text centroids and rotation.
* #param {Node} a
* #param {Number} i
* #return {Number}
*/
function arcTweenText(a, i) {
var oi = d3.interpolate({ x0: a.x0s, x1: a.x1s }, a);
function tween(t) {
var b = oi(t);
return "translate(" + arc.centroid(b) + ")rotate(" + computeTextRotation(b) + ")";
}
return tween;
}
/**
* Calculate the correct distance to rotate each label based on its location in the sunburst.
* #param {Node} d
* #return {Number}
*/
function computeTextRotation(d) {
var angle = (d.x0 + d.x1) / Math.PI * 90;
// Avoid upside-down labels
return (angle < 120 || angle > 270) ? angle : angle + 180; // labels as rims
//return (angle < 180) ? angle - 90 : angle + 90; // labels as spokes
}
</script>
You can't send json-data the way you have shown and achieve what you want.
To do this you have can follow any one mentioned below:
read your json file, deserialize to a POJO and then send deserialized data from a separate controller-endpoint. Make sure you call your ajax method from client end on document ready state.
read your json file, deserialize to a POJO and then send with modelAttribute as you have done i.e model.addAttribute("jsonData", deseriazedData); and read from controller side by JS like: var yourJsonData=${jsonData}, parse to jsonData with JSON.parse(yourJsonData) and then use that for your chart.
But make sure, all events like page loading then generating chart from this data are happening one after another in desired order.
P.S.: search read json file and map to pojo if you find difficulties.
If you are unsure or need more help, then state your json file data structure and your specific problem. I will try to help
Related
During an interview, the tech lead said my scala code was just like java code, but using scala api and he wanted me to improve on that.
I am a 3-year java developer and I began scala coding by following the MOOC on Coursera.
Can anyone tell me what is the problem and how can I improve it, please?
I got the job because of my Java acknowledge but the job is based on scala and the coding style is one thing to fix during the trial period.
object Extraction {
// IntelliJ use .idea/modules as current working directory
val FilePathPre = "../../src/main/resources/"
val UserIdFile = "lookup_user.csv"
val ProductIdFile = "lookup_product.csv"
val RatingFile = "agg_ratings.csv"
def readFile(file: String): Iterator[((String, String), String, String)] = {
val Splitter = ","
Source.fromInputStream(this.getClass.getResourceAsStream(file)).getLines()
.map(_.split(Splitter))
.filter(_.size >= 4) // in case line is not valid
.map(x => ((x(0), x(1)), x(2), x(3))) // (userId, ItemId), rating, time
}
def filePrinter(fileName: String, lines: mutable.Map[String, Int]) = {
val file = new File(fileName)
val bw = new BufferedWriter(new FileWriter(file))
lines.toArray.sortWith((a, b) => a._2 < b._2)
.map(x => x._1 + "," + x._2.toString + "\n")
.foreach(bw.write)
bw.close()
}
def aggFilePrinter(fileName: String, lines: mutable.Map[(Int, Int), Float]) = {
val file = new File(fileName)
val bw = new BufferedWriter(new FileWriter(file))
lines.foreach(x => {
val line = x._1._1.toString + "," + x._1._2.toString + "," + (math.round(x._2 * 100.0) / 100.0).toFloat + "\n"
bw.write(line)
})
bw.close()
}
/**
* * une pénalité multiplicative de 0.95 est appliquée au rating
* pour chaque jour d'écart avec le timestamp maximal de input.csv
*
* #param nowTime maximal timestamp at input.csv
* #param pastTime current rating time
* #param rating original rating
* #return final rating multiplied by 0.95 for every day interval from the maximal timestamp
*/
def finalRating(nowTime: String, pastTime: String, rating: String): Float = {
val now =
LocalDateTime.ofInstant(Instant.ofEpochMilli(nowTime.toLong), ZoneId.systemDefault())
val past =
LocalDateTime.ofInstant(Instant.ofEpochMilli(pastTime.toLong), ZoneId.systemDefault())
val diff = ChronoUnit.DAYS.between(past, now)
(math.pow(0.95, diff) * rating.toFloat).toFloat
}
/**
*
* #param file file to extract
*/
def fileDispatcher(file: String) = {
/**
* get idIndice or increment to idIndice and put it to id map
* #param id id in String
* #param idIndice id in Int
* #param idMap userIdMap or productIdMap
* #return (indice for id, max idIndice)
*/
def getIndice(id: String, idIndice: Int, idMap: mutable.Map[String, Int]): (Int, Int) = {
idMap.get(id) match {
case Some(i) => (i, idIndice)
case None => {
val indice = idIndice + 1
idMap += (id -> indice)
(indice, indice)
}
}
}
// 1. scan the file the find the max time
val maxTime = readFile(file).reduce((a, b) => if(a._3 > b._3) a else b)._3
// 2. apply rating condition, calculate rating and return only valid rating lines
val validLines = readFile(file).map(x => (x._1, finalRating(maxTime.toString, x._3, x._2))).filter(_._2 > 0.01)
// 3. loop file lines, sum ratings by (userId, productId), and combine id_String and id_Int
val userIdMap = mutable.Map[String, Int]() // (userId, userIdAsInt)
val productIdMap = mutable.Map[String, Int]() // (productId, productIdAsInt)
val userProductRatingMap = mutable.Map[(Int, Int), Float]() // (userIdAsInt, productIdAsInt, ratingSum)
var userIdIndice = -1
var productIdIndice = -1
validLines.foreach(x => {
val userIdString = x._1._1
val userId = getIndice(userIdString, userIdIndice, userIdMap)
userIdIndice = userId._2
val productIdString = x._1._2
val productId = getIndice(productIdString, productIdIndice, productIdMap)
productIdIndice = productId._2
val key = (userId._1, productId._1)
userProductRatingMap.get(key) match {
case Some(i) => userProductRatingMap += (key -> (i + x._2))
case None => userProductRatingMap += (key -> x._2)
}
})
filePrinter(FilePathPre + UserIdFile, userIdMap)
filePrinter(FilePathPre + ProductIdFile, productIdMap)
aggFilePrinter(FilePathPre + RatingFile, userProductRatingMap)
}
}
Apart of javish code you have also code style issues, suggest to read https://docs.scala-lang.org/style/ at the start (this is not an ultimate guide, but for start is ok). Avoid to use ._1 on tuples, use match { case (a, b, c) => ... } instead.
The main issue is that you use mutable structures, so in scala every structure is immutable by default and it should stay like that unless you have a strong reason to have it mutable. It is more about functional programming which from one perspective tries to avoid mutability and side effects, you can google for this topic more.
So remove mutable. from your code and replace foreach with eg. foldLeft to get newly created immutable.Map on each iteration instead of modifying existing one.
I have scriptet me a maps with markers based on a MySQL Table.
The position is in the Table.
Now, i would like to write with HTML in the InfoWindow, because it not show HTML on the InfoWindow on the Map.
downloadUrl('inc/map_bridge.php', function(data) {
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName('marker');
Array.prototype.forEach.call(markers, function(markerElem) {
var name = markerElem.getAttribute('name');
var ide = markerElem.getAttribute('id');
var desc = markerElem.getAttribute('beschreibung');
var type = markerElem.getAttribute('art');
var link = '<p>Klicke für weitere infos: Hier';
var point = new google.maps.LatLng(
parseFloat(markerElem.getAttribute('lat')),
parseFloat(markerElem.getAttribute('lng')));
var infowincontent = document.createElement('div');
var strong = document.createElement('strong');
strong.textContent = 'ID' + ide + ': ' + name
infowincontent.appendChild(strong);
infowincontent.appendChild(document.createElement('br'));
var text = document.createElement('text');
text.textContent = desc + link
infowincontent.appendChild(text);
var icon = customLabel[type] || {};
var marker = new google.maps.Marker({
map: map,
position: point,
label: icon.label
});
marker.addListener('click', function() {
infoWindow.setContent(infowincontent);
infoWindow.open(map, marker);
});
});
});
}
It show only so:
show example picture
the following i have in the MySQL-Table
first line<br>second line
If 'beschreibung' were the result of SELECT SQL, the reason is escaped SELECT
SQL result.
(I'm in short of reputation, so write in answer form.)
I have the unenviable task of editing a 2000 line javascript file inorder to maintain and add some new feature to a web app written in JSP, Json-RPC, jQuery and Java. I do not possess any deeper knowledge of jQuery and Json-RPC except basic Javascript knowledge and the original developer is not there anymore.
There is a JS function which accepts a few params, and calls a Json-RPC and here I am getting the error
arg 1 could not unmarshal
Can someone please tell me what this error means?
Here is my code
function distributeQuantityNew(pReportId, pDecimalPlaces, pRun) {
try {
alert('distributeQuantityNew: ' + pReportId + ', ' + pDecimalPlaces + ', ' + pRun);
var fieldValue = $("#distribution_quantity_" + pReportId).val();
if (fieldValue.length == 0) {
showErrorDialog(resourceBundleMap["error.no.distribution.quantity"]);
return;
} else {
$("#distribution_quantity_" + pReportId).val("");
}
var affectedRowIds = [];
var rows = $("#tableBody_" + pReportId + " tr:visible").has("input[type=text]").filter(function(index) {
var voucherType = this.cells[getVoucherColumnIndex()].innerHTML;
if ((voucherType == 'TRANSFER_CS') || (voucherType == 'PAYOUT_CS') || (voucherType == 'SOURCE_BON') || (voucherType == 'PAYOUT_BON')) {
return false;
}
affectedRowIds.push(parseInt(this.id.split("_")[3]));
return true;
}
);
var affectedReportRows = $.extend(true, {}, foreignReportMap[pReportId]);
$.each(affectedReportRows.map, function(i, row) {
if ($.inArray(row.partnerReportBillNr, affectedRowIds) == -1) {
delete affectedReportRows.map["row_" + row.partnerReportBillNr];
}
});
var report = getLoadedReportByRunId(pReportId);
var productType = report.partnerProductType;
SessionManager.extend();
var resultRows = jsonrpc.foreignReportObject.distributeQuantity(affectedReportRows, fieldValue, pDecimalPlaces, pRun);
alert('back after RPC');
$.each(resultRows.map, function(i, row) {
foreignReportMap[pReportId].map["row_" + row.partnerReportBillNr] = row;
updateForeignReportRow(row, true, productType);
});
updateSummaryRow(pReportId);
toggleApproveAllLink(pReportId);
sortForeignReportTable(pReportId, true);
} catch (e) {
handleError("Failed to distribute quantity: ", e);
}
}
I have peppered it with alerts so that I know whether RPC call was succesful, but I get the error arg 1 could not unmarshal before that from the catch block. Thanks for any hints
OK, got it solved. The first parameter to the remote function is expecting a list of Map<String, SomeBO>. SomeBO is a bean with several BigDecimals. I had another JS function which had set the values passed into the Map. This function was setting a BigNumber where I had a setter of String only. I wish the error I had gotten back from JSON unmarshaller was a bit more descriptive...Below is the code where I added .toString() to solve the issue
foreignReportMap[pReportId].map["row_" + pRowId].clientQuantity = clientQuantity.toString();
foreignReportMap[pReportId].map["row_" + pRowId].totalClientQuantity = totalClientQuantity.toString();
I'm currently creating an ArrayList in Java, then running the .toJson function from Google-gson on it:
public String statusesToJson(ArrayList<String> statuses){
Gson gson = new Gson();
return gson.toJson(statuses);
}
Which results in the JSON:
[ "u", "u", "u", "u" ]
Then in JSP I'm passing it into JavaScript:
<script language="JavaScript" type="text/javascript">CheckStatus.loaded('<%=model.getPageId() %>', '<%=request.getContextPath() %>', '<%=model.arrayListToJson(model.getStatuses()) %>');</script>
Then in the JavaScript I'm parsing it to a JSON array:
CheckStatus.statuses = JSON.parse(statuses);
alert(CheckStatus.statuses);
This then results in the following output:
u, u, u, u
The problem is that the following doesn't work and causes my page to not load:
alert(CheckStatus.statuses[0]);
What's wrong with this?
EDIT:
Loaded Function:
loaded : function(guid, context, statuses) {
CheckStatus.guid = guid;
CheckStatus.context = context;
CheckStatus.statuses = JSON.parse(statuses);
alert(CheckStatus.statuses[0]);
if(CheckStatus.init == null){
submitForm('checkStatusForm', CheckStatus.guid);
CheckStatus.init = true;
}
setupForm('checkStatusForm', function(){CheckStatus.validStatus();});
//CheckStatus.setImages();
applyCSS3('Check_Status');
}
Valid Status Function:
validStatus : function(){
CheckStatus.params = $('#checkStatusForm').serializeObject();
if(document.getElementById('regionID').value != "" && document.getElementById('regionAction').value != ""){
submitForm('checkStatusForm', CheckStatus.guid);
}else{
error("Cannot Commit", "You must select an action before attempting to commit.");
}
},
Setup Form Function:
/**
* Sets up the form to submit when the user presses enter inside an input
* element. Also calls the callback when the form is submitted, does not
* actually submit the form.
*
* #param id The id of the form.
* #param callback The callback to call.
* #return Nothing.
*/
function setupForm(id, callback) {
$('#' + id + ' input').keydown(function(e) {
if (e.keyCode === 13) {
$(this).parents('form').submit();
e.preventDefault();
}
});
$('#' + id).submit(function(e) {
e.preventDefault();
callback();
});
}
Submit Form Function:
/**
* Serializes and submits a form.
*
* #param id
* The id of the form to submit.
* #param guid
* The guid of the page the form is on to pass to the server.
* #return nothing.
*/
function submitForm(id, guid) {
var subTabId = $('#' + id).closest('#tabs > div > div').attr(
'id'), tabId = $('#' + id).closest('#tabs > div')
.attr('id'), data = $('#' + id).serializeArray();
data.push( {
name : "framework-guid",
value : guid
});
$.ajax( {
type : 'POST',
cache : 'false',
url : '/pasdash-web/' + tr("_", "", tabId.toLowerCase()) + '/' + tr("_", "", subTabId)
+ '.jsp',
data : data,
success : function(html) {
$('#' + subTabId).html(html);
resourceChanged(tabId, subTabId,
$('#' + id + ' input[name="framework_command"]')[0].value,
guid);
},
error : function(jqXHR, textStatus, errorThrown) {
error('Ajax Error', textStatus);
}
});
return false;
}
You don't need to wrap your JSON with strings, that will just force you to have to reparse it. I would try removing those quotes and not calling JSON.parse
loaded : function(guid, context, statuses) {
CheckStatus.guid = guid;
CheckStatus.context = context;
// Here's the change
CheckStatus.statuses = statuses;
alert(CheckStatus.statuses[0]);
And change your HTML to be
<script type="text/javascript">
CheckStatus.loaded('<%=model.getPageId() %>',
'<%=request.getContextPath() %>',
// the following line should output something like
// ["a", "b"]
// which is perfectly valid JavaScript
<%=model.arrayListToJson(model.getStatuses()) %>);
</script>
You should be able to write:
CheckStatus.loaded('<%=model.getPageId() %>', '<%=request.getContextPath() %>', <%=model.arrayListToJson(model.getStatuses()) %>);
without the quotes around the last argument. Then, that "loaded()" function will get the object directly and there'll be no need to call "JSON.parse()".
Check the type of the result of JSON.parse (). Seems to me that it is a string and not an array. Maybe a pair of quotes somewhere that should not be there?
I've been syncing some text with audio, using jPlayer and jQuery's .fadeIn/.fadeOut. It works as expected, using these if statements:
var currentTime = Math.floor(event.jPlayer.status.currentTime)
if (currentTime > 1){
$("#slide1").fadeIn("fast");
} else if (currentTime < 1){
$("#slide1").fadeOut("fast");
}
if (currentTime >= 9 && currentTime < 49){
$("#slide2").fadeIn("fast");
} else if (currentTime < 9){
$("#slide2").fadeOut("fast");
}
and so on. This will show/hide div's at the proper time and allow for scrubbing. The code I have adapted is from here. I am a designer who knows a little programming, so my skills aren't great but I understand a lot. I'm looking for a more efficient way to do this, instead of copy/pasting the if statement as there will be A LOT of them. Possibly an array of all the in/out times? I'm not sure how to do it.
Best case scenario would be to have an xml list of times control prebuilt divs (I think anyway). Any ideas on how to do it?
Thanks for your time,
S
Update
Ok, I've made some progress, but am having problems with the XML/loop. I have added content to the XML and have it dynamically generating divs. Here is the xml:
<?xml version="1.0" encoding="utf-8" ?>
<Slides>
<Slide id="#slide1" in="1" out="9999" content="Implications"></Slide>
<Slide id="#slide2" in="9" out="49" content="Implications have the form"></Slide>
<Slide id="#slide3" in="11" out="49" content="If A is true, then B is true."></Slide>
</Slides>
and here is my code that creates the divs (that are hidden)
<script>
var mySlides = [];
$.ajax({
type: "GET",
url: "xml.xml",
dataType: "xml",
success: parseXml
});
function parseXml(xml)
{
$(xml).find("Slide").each(function() {
$("#slides").append("<div class=\"starthidden\" id=" + $(this).attr("id") + ">" + $(this).attr("content") + "</div>");
});
var $slides = xml.find('Slide');
for (var i = 0, len = $slides.length; i < len; i++) {
mySlides[mySlides.length] = {
in : $slides.eq(i).attr('in'),
out : $slides.eq(i).attr('out')
};
}
}
$("#audio").bind($.jPlayer.event.timeupdate, function(event) {
var currentTime = Math.floor(event.jPlayer.status.currentTime)
for (var i = 0; i < len; i++) {
if (currentTime >= mySlides[i].in && currentTime < mySlides[i].out) {
$(mySlides[i].id).fadeIn("fast");
} else if (currentTime < mySlides[i].in) {
$(mySlides[i].id).fadeOut("fast");
}
}
});
</script>
I guess I just don't really know where to go from here to get the divs to show/hide now? As well, I've got to figure out a way to turn many divs off as well, as slides are actually lines, not full slides so I need to clear them off the stage (if you see in the XML, it would be at 49sec). Can anyone help? Thanks so much.
S
You can setup an array of objects that store information for each slide.
//setup an array of objects that store information for each caption
var mySlides = [
{
id : '#slide1',//set a string reference to the element to fade
in : 1,//start time
out : 9999//end time (this is long so it doesn't fade out ever)
},
{
id : '#slide2',
in : 9,
out : 49
}
], len = mySlides.length;
//loop through the slides to check if they should be faded in/out
for (var i = 0; i < len; i++) {
if (currentTime >= mySlides[i].in && currentTime < mySlides[i].out) {
$(mySlides[i].id).fadeIn("fast");
} else if (currentTime < mySlides[i].in) {
$(mySlides[i].id).fadeOut("fast");
}
}
Update
If your XML file is something like this:
<slides>
<slide id="#slide1" in="1" out="9999"></slide>
<slide id="#slide2" in="9" out="49"></slide>
</slides>
Then you can grab the XML file using AJAX and parse it for it's data like this:
var mySlides = [];
$.ajax({
url : 'path/to/file.xml',
type : 'get',
dataType : 'xml',
success : function (serverResponse) {
var $slides = serverResponse.find('slide');
for (var i = 0, len = $slides.length; i < len; i++) {
mySlides[mySlides.length] = {
id : $slides.eq(i).attr('id'),
in : $slides.eq(i).attr('in'),
out : $slides.eq(i).attr('out')
};
}
}
});
with your page content i would include some JSON (using Japser's structure, forget XML) detailing the messages and their in/out points..
then, in a handler for jPlayer's timeupdate event (called every 250ms), examine event.jPlayer.status.currentTime and determine which message from your JSON should currently be displayed.