pChart Radar chart with minimum 1 - pchart

I'm using a Radar Chart similar to this example:
https://pchart.net/doc.draw.radar.html
My data ranges from score 1 to 4, so I configured some options:
$options = array(
'SegmentHeight' => 1,
'Segments' => 3,
'FixedMax' => 4
);
One problem remains: Even if my lowest score is 1, the Radar chart always has the value 0 in the center.
How can I change the minimum value in the chart to 1?

You can create a "minimum" value, but only if you're willing to fork the library and make some changes.
Game plan
The easiest and most straightforward way to do so is to create a new option that I'll call FixedMin. If, and only if, this new option is provided (and the other criteria for non-auto-segmentation are met) will you achieve the effect you're seeking.
If you want a minimum to be generated without providing SegmentHeight, Segments, and FixedMax, you'll also need to modify pImage::computeScale which generates these configuration values when they're not provided.
We need to make three changes:
Create a new configuration option called FixedMin
Adjust the values' position
Adjust the labels' text
Let's code
The drawRadar method is held in class/pRadar.class.php. Open it up. Let's walk through our game plan.
First, let's add the configuration option. I'll add it with the others (line ~38) like so:
$FixedMin = isset($Format["FixedMin"]) ? $Format["FixedMin"] : 0;
Let's have FixedMin default to 0 because that is the default pChart behavior.
Second, we need to somehow trick pChart into repositioning these larger values as if they were smaller to accomodate the offset FixedMin creates.
We can do that where the function computes the plots position (line ~319). Find the loop foreach($DataS["Data"] as $Key => $Value) (line ~328). Here, we'll modify the $Value by adding this line at the top of the loop:
$Value -= $FixedMin; // Let's offset the perceived value by our new minimum
Third, we need to change the axis labels' text values. Inside the conditional that $DrawAxisValues encompasses, you'll find this line of code (line ~255):
$Label = ($j * $SegmentHeight)
This works great; for each segment, it generates a label that is the segment times the height (e.g., 4 segments of 20 units should generate 4 labels with: 20, 40, 60, 80). But it won't work with our FixedMin. Let's reuse the offset. In this case, we're incrementing by the offset, to generate the illusion of a minimum. Replace that line with this one:
$Label = ($j * $SegmentHeight) + $FixedMin;
Recap
We created a new configuration variable for a minimum segment; and for each value, we subtracted that minimum value (or offset); and rejiggered the axis labels by adding that minimum value (or offset).
Caveats
We only modified radar charts; no other chart type will be affected. Do not attempt to use data with values that are below the configured minimum. The hazards of doing so may be why the author didn't include this option.
Show and tell
I don't have access to your data set, so I used the pChart example and bumped the "application review" sample so that all the scores were between 20 and 40.
// FixedMin set to 0, "default" behavior
$Options = array(
'SegmentHeight' => 20,
'Segments' => 2,
'FixedMax' => 40,
'FixedMin' => 0, // And so on...
As expected, now let's check out our new code...
// FixedMin set to 20, smaller SegmentHeight
$Options = array(
'SegmentHeight' => 10,
'Segments' => 2,
'FixedMax' => 40,
'FixedMin' => 20, // And so on...
Ta-da.

Related

Google Sheets API (v4) - `AutoResizeDimensions` not working

I've got a system that generates and automatically maintains lots of spreadsheets on a Drive account.
Whenever I add data to the sheet I run a 'format' method to pass over and make sure everything is ok.
This generally does things like:
set the default font and size across the sheet
set up the heading row
freeze rows
In addition, I have the code below to make sure the first two columns (index 0 and 1) in the sheet are autoresizing to fit their contents. when I run it though, this element doesn't seem to make a difference. The font, column freezes etc all work.
Other notes:
I only want those 2 columns to auto-resize
the amount of rows in a sheet can vary
this job is appended to the end of several in requestList
My code:
requestList.Requests.Add(new Google.Apis.Sheets.v4.Data.Request()
{
AutoResizeDimensions = new AutoResizeDimensionsRequest()
{
Dimensions = new DimensionRange()
{
SheetId = Convert.ToInt32(sheetId),
Dimension = "COLUMNS",
StartIndex = 0,
EndIndex = 1
}
}
});
var updateRequest = sheetService.Spreadsheets.BatchUpdate(requestList, spreadSheetId);
var updateResponse = updateRequest.Execute();
Could the order which I request the 'format' changes be affecting things maybe? Can anyone help?
As written in the documentation,
the start index is inclusive and the end index is exclusive.
So, For the first two columns, it should be
startIndex = 0,
endIndex = 2

how do i update a Google Charts pie chart to reflect filtered data but retain filtered out segments as a diff colour

I'm new to Google Charts and I'm struggling to solve this.
I have a datatable (called "result" in the code)
Name Liquidity percent
a 1.3 20%
b 2.0 20%
c 3.4 20%
d 4 20%
e 5 20%
My pie chart is set to show 5 segments of equal size - 20% - and each segment is blue
I have set a 'Number Range Filter' control wrapper to filter the liquidity - when i set the control to the range 1 to 4 the pie moves to 4 equal sized segments.
BUT... I don't want it to do this. Instead of 1 segment disappearing I want the 5 segments to remain visible and the colour of the filtered segment to change to be a different colour.
The aim being that I can see visually a total percentage that falls within the number filter.
EDIT:
So I've had a mess about and this is as far as I've got incorporating dlaliberte's comment below.
function drawChart3(chartData3) {
var result = google.visualization.arrayToDataTable(chartData3,false); // 'false' means that the first row contains labels, not data.
var chart3 = new google.visualization.ChartWrapper({
'chartType': 'PieChart',
'containerId': 'chart3_div',
'dataTable': result,
'options': {
'width': 500,
'height': 500,
'legend': {position: 'none'},
'pieSliceText': 'none',
'colors': ['blue']
},
'view': {'columns': [0 , 1]}
});
var liquidityDT = new google.visualization.DataTable();
// Declare columns
liquidityDT.addColumn('number', 'Liquidity');
// Add data.
liquidityDT.addRows([
[1],
[2],
[3],
[4],
[5],
]);
// Create a range slider, passing some options
var liquidityRangeSlider = new google.visualization.ControlWrapper({
'controlType': 'NumberRangeFilter',
'containerId': 'filter3_div',
'dataTable': liquidityDT,
'options': {
'filterColumnLabel': 'Liquidity',
'minValue': 0,
'maxValue': 5
}
});
liquidityRangeSlider.draw();
chart3.draw();
google.visualization.events.addListener(liquidityRangeSlider, 'statechange', setChartColor);
function setChartColor(){
var state = liquidityRangeSlider.getState();
var stateLowValue = state.lowValue;
var stateHighValue = state.highValue;
for (var i = 0; i < result.getNumberOfRows(); i++) {
var testValue = result.getValue(i,2);
if (testValue < stateLowValue || testValue > stateHighValue){
alert("attempting to set colors")
//this bit I have no clue how to change the color of the table row currently being iterated on
chart3.setOption({'colors' : ['red']});
}
}
chart3.draw();
}
}
so it produces the pie chart with 5 blue segments. I can move the number filter and it fires the listener event but I can't get it to affect anything on the piechart (Chart3) - The code currently attempts just to change the whole chart to RED but that isn't even working never mind the just colouring the filtered segments
So how do I effect the changes into Chart3 and how do I only effect the filtered segments?.
any clues welcome?
thanks
You can (and must, for your application) use a NumberRangeFilter outside of a Dashboard because it will otherwise always do the data filtering. Instead, just listen for the change events on the NumberRangeFilter, get its current state, and then iterate through the colors array for your chart to set the appropriate colors. You'll have to draw the chart again with the updated colors. Here is the loop to set the colors and redraw.
var colors = [];
for (var i = 0; i < result.getNumberOfRows(); i++) {
var color = (testValue < stateLowValue || testValue > stateHighValue) ? 'red' : 'blue';
colors.push(color);
}
chart3.setOption('colors', colors);
chart3.draw();

How can I make OxyPlot adjust zoom when calling PlotView.Invalidate?

I have a problem with OxyPlot which is as follows:
I create a new PlotView and attach a PlotModel with some axes when my program starts.
In my program, the user can open a file, which is interpreted and plotted in the PlotView control.
To display the new Series, I do
myPlotView.Invalidate(true);
This will display the data on in the plot, however OxyPlot does not perform any zooming. How can I pan and zoom automatically, such that the plot covers the whole PlotView?
I tried to set
myPlotView.Model.Axes[0].DataMinimum = someValue1
myPlotView.Model.Axes[1].DataMinimum = someValue2
myPlotView.Model.Axes[0].DataMaximum = someValue3
myPlotView.Model.Axes[1].DataMaximum = someValue4
but nothing happens.
Axes calculates the ViewMinimum and ViewMaxium (the real thing you are watching) only if Minimum and Maximum are not specified, you can set them to Double.NaN before resetting, so the ViewMinimum and ViewMaximum will be calculated based on DataMinimum and DataMaximum
foreach (var ax in _plotModel.Axes)
ax.Maximum = ax.Minimum = Double.NaN;
PlotView.ResetAllAxes();
Also if you changed the data points, you must call to Plot.InvalidatePlot() so the DataMinimum and DataMaximum gets updated too.
Do a Axis reset and update the Minimum/Maximum of each Axis.
e.g.
PlotView.ActualModel.Axes[0].Reset();
PlotView.ActualModel.Axes[1].Reset();
PlotView.ActualModel.Axes[0].Minimum = 50;
PlotView.ActualModel.Axes[0].Maximum = 250;
PlotView.ActualModel.Axes[1].Minimum = 50;
PlotView.ActualModel.Axes[1].Maximum = 250;
PlotView.InvalidatePlot(true);
you should of course use the min/max value from your data.

Split text file into several parts by character

I apologise in advance if there is already an answer to this problem; if so please just link it (I have looked, btw! I just didn't find anything relating to my specific example) :)
I have a text (.txt) file which contains data in the form 1.10.100.0.200 where 1, 10, 100, 0 and 200 are numbers storing the map terrain layout of a game. This file has multiple lines of 1.10.100.0.200 where each line represents an item of terrain in the map.
Here is what I would like to know:
How do I find out how many lines there are, so I know how many items of terrain to create when I read the map file?
What is the method I should use to get each of 1, 10, 100, 0 and 200:
E.g. when I am translating the file into a map terrain at runtime I might use the terrainitem1.Location = New Point(x, y) or terrainitem1.Size = New Size(p, q) commands, where x, y, p and q are integers or doubles relating to the terrain's location or size. Where would I then find x, y etc. out of 1, 10, 100, 0 and 200, if say x is equal to 1, y to 10 and so on?
I am sorry if this isn't clear, please just ask me and I'll try to explain.
N.B. I am using VB.NET WinForms
There is no way to know how many lines a file has without opening the file and reading its contents.
You didn't indicate how far you've got on this. Do you know how to open a file?
Here's some basic code to do what you want. (Sorry, this is C# but the idea is the same in VB.)
string line;
using (TextReader reader = File.OpenText(#"C:\filename.txt"))
{
// Read each line from the file (until null returned)
while ((line = myTextReader.ReadLine()) != null)
{
// Get each number in line (as string)
string[] values = line.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
// Convert each number to integer
id = int.Parse(values[0]);
height = int.Parse(values[1]);
width = int.Parse(values[2]);
x = int.Parse(values[3]);
y = int.Parse(values[4]);
}
}

Struggling with add.Series (CSV dataset)

I’ll try to keep this short. I have a CSV with traffic counts for specific streets. So far I have plotted the street names on the (x) axis, and the total traffic count on the (y) axis. The CSV also contains counts for vehicles that travel for (< 15 min, 15-30 min, 30-45 min, 60 min, etc).
What I am trying to do is “split” the total count for each street in accordance with the (< 15, 15-30, etc) minute counts, kind of like categories. Essentially, I am trying to replicate this example:
http://dimplejs.org/examples_viewer.html?id=bars_vertical_grouped_stacked where the “Owner” category is instead the “Arterial” category from my dataset.
In short:
1. I can semi-successfully split some of the street names, however, some don’t seem to be split at all even though counts exist for the categories.
The tooltip is not showing category-specific counts. It seems to be shoving all of the counts into one tooltip regardless of hovering over a category.
For the legend, is there a way to ensure that it uses the street names? If I remove the “Commute” values and leave “Arterial” it uses the names correctly, but then I lose the ability to show the categories.
I hope this isn’t too confusing. I’d sincerely appreciate any help.
CODE:
var svg = dimple.newSvg("#chartContainer", 1280, 720);
d3.csv("../HTML/strippedData_v2.csv", function (data) {
var myChart = new dimple.chart(svg, data);
myChart.setBounds(60, 45, 510, 315)
myChart.addCategoryAxis("x", ["Arterial"]);
myChart.addMeasureAxis("y", "Total");
myChart.addSeries(["Arterial", "Commute15", "Commute1530", "Commute3045", "Commute4560", "Commute60"], dimple.plot.bar);
myChart.addLegend(200, 10, 380, 20, "right");
myChart.draw();
});
IMAGES: (Don't have enough rep :/)
(Only the first 3 images of the gallery apply.)
http://imgur.com/a/8P2tN#0
I'm struggling to work out exactly how you would like the chart to look. I suspect the problem may be the CommuteXX fields. It sounds like you are trying to treat them as dimension values, whereas dimple treats columns as dimensions (and their row values as dimension values). Therefore you need to reorganise your data something like this:
Arterial |Commute |Population
Colfax Avenue |Commute15 |1380
Colfax Avenue |Commute1530 |1641
Colfax Avenue |Commute3045 |855
Etc...
This can be done in Javascript once the CSV is loaded. Here is a function to do that:
function unPivot(sourceData, valueFields, newCategoryField, newValueField) {
var returnData = [],
newRow,
key,
i,
j;
for (i = 0; i < sourceData.length; i += 1) {
for (j = 0; j < valueFields.length; j += 1) {
newRow = {}
for (key in sourceData[i]) {
if (sourceData[i].hasOwnProperty(key) && valueFields.indexOf(key === -1)) {
newRow[key] = sourceData[i][key];
}
}
newRow[newCategoryField] = valueFields[j];
newRow[newValueField] = sourceData[i][valueFields[j]];
returnData.push(newRow);
}
}
return returnData;
};
And here it is in a fiddle: http://jsfiddle.net/GeLng/15/
I'm not sure if this is the chart you are looking for, you mention a grouped bar but I'm not sure what you want to group by. Hopefully this will give you enough to create the chart the way you want.