Error in looping and rendering JSON data from API - vue.js

I want to to render the JSON values from an API using vue and vue-chartjs. The bar-chart instead of looping through the array and plot the complete array, shows just the first two results (dc:title and dc:identifier).
Using JQUERY I have integrated the script into my chart.vue component
<script>
import Chart from 'chart.js';
import JQuery from 'jquery'
let $ = JQuery
export default {
name: 'app',
mounted() {
var chart = this.$refs.chart;
const ctx = chart.getContext("2d");
const myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: [],
datasets: [{
label: '# of Metadata',
data: [],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
var getData = function() {
$.ajax({
url: 'http://localhost:3000/ratio/total',
success: function(data) {
console.log(data[0].results);
for(var key in data[0].results[0]){
myChart.data.labels.push(data[0].results[key][0]);
myChart.data.datasets[0].data.push(data[0].results[key][1]);
}
myChart.update();
}
});
};
// get new data every 3 seconds
getData();
}
}
</script>
API JSON output
[
{
"results": [
[
"dc:title",
553
],
[
"dc:identifier",
553
],
[
"dc:subject",
553
],
[
"dc:type",
553
],
[
"dc:format",
553
],
[
"dc:description",
553
],
[
"dc:language",
553
],
[
"$",
553
],
[
"dc:relation",
553
],
[
"dc:source",
532
],
[
"dc:rights",
449
],
[
"dc:date",
21
],
[
"dc:creator",
21
],
[
"dc:publisher",
21
],
[
"dc:coverage",
21
],
[
"pico:anchor",
0
],
[
"pico:preview",
0
],
[
"dcterms:rightsHolder",
0
],
[
"pico:author",
0
],
[
"pico:materialAndTechnique",
0
],
[
"dc:isReferencedBy",
0
],
[
"dcterms:isReferencedBy",
0
],
[
"dcterms:spatial",
0
],
[
"dcterms:created",
0
],
[
"pico:producer",
0
],
[
"dcterms:medium",
0
],
[
"dcterms:extent",
0
],
[
"pico:distributor",
0
],
[
"dcterms:isPartOf",
0
],
[
"dcterms:license",
0
],
[
"pico:licenseMetadata",
0
],
[
"dcterms:alternative",
0
],
[
"dcterms:modified",
0
],
[
"dcterms:hasPart",
0
],
[
"pico:contact",
0
],
[
"pico:information",
0
],
[
"pico:service",
0
],
[
"pico:responsible",
0
],
[
"pico:isManagedBy",
0
],
[
"dcterms:provenance",
0
]
]
}
]

I don't think that is a valid object first off.
"results": [["dc:title",553], should be "results": [{"dc": "title", "key": 553}]
notice the curly braces { and the key missing from the second numeric prop
check this out: https://www.w3schools.com/js/js_json_objects.asp
you can run ur json through a validator as well to make sure: https://jsonformatter.curiousconcept.com/

Related

Rendering mpld3 JSON chart in React

Good day.
I just recently came across mpld3 and I am trying to see if it will be the solution to my issue.
I want to create Matplotlib charts in Python, save them as JSON objects to a database, and then render them in my React webpage.
So far I have created a plot in Python, using Matplotlib and converted it to a JSON object in a dictionary:
fig = plt.gcf()
mpld3.display(fig)
fig_to_dict = mpld3.fig_to_dict(fig)
Now I can save the dictionary to a file.
My question is: Can I then render this JSON chart in my React project? And if so, how do I do that?
Another thing is - I cannot use D3 in the front-end, I need to do all my "chart making" in the back-end.
Thank you for any and all assistance!
This is not a perfect answer, but I seemed to be solving a similar problem and I appear to have got it working so am sharing my code as is. Hopefully it will prove useful to someone.
This is my react component ('matPlotLibFig.js') that renders a simple figure
import React from 'react'
import mpld3_load_lib from "./mpld3_load_lib";
import mpld3 from 'mpld3'
import _json from "./plot_b.json"
const MatPlotLibFig = () => {
const fig_name = "fig_el427345810798888193429725"
return <div>
<script>
mpld3_load_lib("https://d3js.org/d3.v5.js", function () {
mpld3_load_lib("https://mpld3.github.io/js/mpld3.v0.5.8.js", function () {
mpld3.remove_figure(fig_name)
mpld3.draw_figure(fig_name, _json);
})
});
</script>
<div id={fig_name}></div>
</div>
}
export default MatPlotLibFig
plot_b.json:
{
"width": 640.0,
"height": 480.0,
"axes": [
{
"bbox": [0.125, 0.11, 0.775, 0.77],
"xlim": [-0.5, 10],
"ylim": [0, 10],
"xdomain": [-0.2, 10],
"ydomain": [0.0, 10],
"xscale": "linear",
"yscale": "linear",
"axes": [
{
"position": "bottom",
"nticks": 11,
"tickvalues": null,
"tickformat_formatter": "",
"tickformat": null,
"scale": "linear",
"fontsize": 10.0,
"grid": {
"gridOn": false
},
"visible": true
},
{
"position": "left",
"nticks": 11,
"tickvalues": null,
"tickformat_formatter": "",
"tickformat": null,
"scale": "linear",
"fontsize": 10.0,
"grid": {
"gridOn": false
},
"visible": true
}
],
"axesbg": "#FFFFFF",
"axesbgalpha": null,
"zoomable": true,
"id": "el91906139874005984112",
"lines": [
{
"data": "data01",
"xindex": 0,
"yindex": 1,
"coordinates": "data",
"id": "el91906139873968544448",
"color": "#000000",
"linewidth": 1.5,
"dasharray": "none",
"alpha": 1,
"zorder": 2,
"drawstyle": "default"
}
],
"paths": [],
"markers": [
{
"data": "data01",
"xindex": 0,
"yindex": 1,
"coordinates": "data",
"id": "el91906139873968544448pts",
"facecolor": "#000000",
"edgecolor": "#FFFFFF",
"edgewidth": 5,
"alpha": 1,
"zorder": 2,
"markerpath": [
[
[
-10.0,
10.0
],
[
10.0,
10.0
],
[
10.0,
-10.0
],
[
-10.0,
-10.0
]
],
[
"M",
"L",
"L",
"L",
"Z"
]
]
}
],
"texts": [],
"collections": [],
"images": [],
"sharex": [],
"sharey": []
}
],
"data": {
"data01": [
[
0.0,
6.0
],
[
1.0,
2.0
],
[
2.0,
8.0
],
[
3.0,
3.0
],
[
4.0,
0.0
],
[
5.0,
7.0
]
]
},
"id": "el91906139874196422128",
"plugins": [
{
"type": "reset"
},
{
"type": "zoom",
"button": true,
"enabled": false
},
{
"type": "boxzoom",
"button": true,
"enabled": false
}
]
}
mpld3_load_lib.js:
const mpld3_load_lib = (url, callback) => {
var s = document.createElement('script');
s.src = url;
s.async = true;
s.onreadystatechange = s.onload = callback;
s.onerror = function () { console.warn("failed to load library " + url); };
document.getElementsByTagName("head")[0].appendChild(s);
}
export default mpld3_load_lib
Which renders a dynamic plot that looks like

Draw vertical thresholds with chartjs-plugin-annotation and vue-chartjs

I try to position 2 vertical lines which are my thresholds for list of results for some tests.
My problem is that when I try to take value which is not part of the results, The lines are not shown.
With the following code I see the lines.
<script>
import BarChart from "#/components/TestsStatictics/BarChart.vue";
export default {
components: {
BarChart,
},
data() {
return {
chartData: {
labels: [24.35, 24, 24.2, 24.28],
datasets: [
{
label: "Bar Chart",
backgroundColor: [
"rgba(255, 99, 132, 0.2)",
"rgba(54, 162, 235, 0.2)",
"rgba(255, 206, 86, 0.2)",
"rgba(75, 192, 192, 0.2)",
"rgba(75, 192, 192, 0.2)",
],
borderColor: [
"rgba(255,99,132,1)",
"rgba(54, 162, 235, 1)",
"rgba(255, 206, 86, 1)",
"rgba(75, 192, 192, 1)",
"rgba(75, 192, 192, 0.2)",
],
pointBorderColor: "#2554FF",
data: [24.35, 24, 24.2, 24.28],
},
],
},
options: {
annotation: {
annotations: [
{
type: "line",
mode: "vertical",
scaleID: "x-axis-0",
value: 24.35,
borderColor: "green",
borderWidth: 1,
label: {
enabled: true,
position: "center",
content: "22.70",
},
},
{
type: "line",
mode: "vertical",
scaleID: "x-axis-0",
value: 24.28,
borderColor: "green",
borderWidth: 1,
label: {
enabled: true,
position: "center",
content: "25.70",
},
},
],
},
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
},
gridLines: {
display: true,
},
},
],
xAxes: [
{
gridLines: {
display: false,
},
barThickness: 30,
},
],
},
legend: {
display: true,
},
responsive: true,
maintainAspectRatio: false,
},
};
},
};
</script>
but if I change the part of the annotation to this (the value of the annotation is not within the data and labels values), It doesn`t work:
annotations: [
{
type: "line",
mode: "vertical",
scaleID: "x-axis-0",
value: 22,
borderColor: "green",
borderWidth: 1,
label: {
enabled: true,
position: "center",
content: "22",
},
},
{
type: "line",
mode: "vertical",
scaleID: "x-axis-0",
value: 26,
borderColor: "green",
borderWidth: 1,
label: {
enabled: true,
position: "center",
content: "26",
},
},
],
Try setting max an min values for the axes:
suggestedMax, suggestedMin
scales: {
xAxes: [
{
gridLines: {
display: false,
},
ticks: {
suggestedMax: 35,
suggestedMin: 20
},
barThickness: 30,
},
],

How to add a tooltip to a d3.js path?

I am building a floormap from geojson data using d3.js topo and d3.js path. For clarification, I am also using vue.js. I want to add a tooltip when the user hovers over a room (aka the d3.js path). First I added just a console log to when the user hovers over a path but that did not work. I noticed that every time I load the app it does a console log but not when a user clicks / hovers over the d3.js path. I heard someone say that I would have to create an invisible circle or rectangle which would have the tooltip property bind to it but I don't think that route would work once the floor maps get complex. I dont care about adding any actual data to the tooltip right now but later I would want to. Can someone point me in the right direction, please? Thank you.
const data = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
0, 0
],
[
0, 11.4
],
[
7, 11.4
],
[
7, 0
],
[
0, 0
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
7, 0
],
[
7, 11.4
],
[
12, 11.4
],
[
12, 0
],
[
7, 0
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
12, 0
],
[
12, 11.4
],
[
19, 11.4
],
[
19, 0
],
[
12, 0
]
]
]
}
}
]
};
var svg = d3.select("svg")
var width = +svg.attr("width")
var height = +svg.attr("height")
svg.attr("transform", "translate(" + width / 2 + ",0)")
var projection = d3.geoIdentity().fitSize([width, height], data)
var path = d3.geoPath(projection)
svg.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("d", path)
.attr("fill", "grey")
.attr("fill-opacity", .2)
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.on("mouseover", console.log("hello"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="200"></svg>
There are several ways to do this. The simplest - but most limited - one is to create a <title> element in each path. It will show on hover, as it's browser native.
const data = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": { "name": "Kitchen" },
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
0, 0
],
[
0, 11.4
],
[
7, 11.4
],
[
7, 0
],
[
0, 0
]
]
]
}
},
{
"type": "Feature",
"properties": { "name": "Living room" },
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
7, 0
],
[
7, 11.4
],
[
12, 11.4
],
[
12, 0
],
[
7, 0
]
]
]
}
},
{
"type": "Feature",
"properties": { "name": "Toilet" },
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
12, 0
],
[
12, 11.4
],
[
19, 11.4
],
[
19, 0
],
[
12, 0
]
]
]
}
}
]
};
var svg = d3.select("svg")
var width = +svg.attr("width")
var height = +svg.attr("height")
svg.attr("transform", "translate(" + width / 2 + ",0)")
var projection = d3.geoIdentity().fitSize([width, height], data)
var path = d3.geoPath(projection)
svg.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("d", path)
.attr("fill", "grey")
.attr("fill-opacity", .2)
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.append("title")
// Do this to maintain access to the features you drew
.datum(function(d) { return d; })
.text(function(d) {
return d.properties.name;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="200"></svg>
More complicated ones can use an absolutely positioned div, for example:
const data = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"name": "Kitchen"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
0, 0
],
[
0, 11.4
],
[
7, 11.4
],
[
7, 0
],
[
0, 0
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Living room"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
7, 0
],
[
7, 11.4
],
[
12, 11.4
],
[
12, 0
],
[
7, 0
]
]
]
}
},
{
"type": "Feature",
"properties": {
"name": "Toilet"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
12, 0
],
[
12, 11.4
],
[
19, 11.4
],
[
19, 0
],
[
12, 0
]
]
]
}
}
]
};
var svg = d3.select("svg")
var width = +svg.attr("width")
var height = +svg.attr("height")
var tooltip = d3.select("#tooltip")
svg.attr("transform", "translate(" + width / 2 + ",0)")
var projection = d3.geoIdentity().fitSize([width, height], data)
var path = d3.geoPath(projection)
svg.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("d", path)
.attr("fill", "grey")
.attr("fill-opacity", .2)
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.on("mousemove", function(d) {
// +3 as some offset to make sure spawning the tooltip doesn't
// accidentally also cause unhover and thus removing itself
tooltip
.html(d.properties.name)
.style("display", "block")
.style("left", d3.event.x + 3 + 'px')
.style("top", d3.event.y + 3 + 'px');
})
.on("mouseleave", function() {
tooltip
.style("display", null)
.style("left", null)
.style("top", null);
});
#tooltip {
position: absolute;
top: 0;
left: 0;
display: none;
border: solid 1px red;
padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="200"></svg>
<div id="tooltip"></div>

Different color for each polygon in multipolygon in mapbox

In the below code you will find a Multipolygon made of 3 polygons mapped in Mapbox.
I would like to fill the 3 polygons with differnet colors.
How do I do that?
If I use the property fill with 1 color, obvisouly it paints the 3 polygons with the same color.
I tried to pass a 3 element list to the 'fill-color' property but it threw an arror.
An easy solution would be to create 3 different layers but this is not viable in my case. In need to use the multipolygon
Any idea?
threeHouses = {
"type": "MultiPolygon",
"coordinates": [
[
[
[
115.813867,
-31.932177
],
[
115.813867,
-31.932177
],
[
115.813867,
-31.932087
],
[
115.813962,
-31.932087
],
[
115.813962,
-31.932124
],
[
115.814005,
-31.932124
],
[
115.814005,
-31.932168
],
[
115.813962,
-31.932168
],
[
115.813962,
-31.932177
],
[
115.813867,
-31.932177
]
]
], [
[
[
115.813962,
-31.932087
],
[
115.813894,
-31.932087
],
[
115.813894,
-31.932042
],
[
115.81391,
-31.932042
],
[
115.81391,
-31.931967
],
[
115.813984,
-31.931967
],
[
115.813984,
-31.932042
],
[
115.81401,
-31.932042
],
[
115.81401,
-31.932087
],
[
115.813962,
-31.932087
]
]
], [
[
[
115.81391,
-31.931967
],
[
115.81391,
-31.931931
],
[
115.813849,
-31.931931
],
[
115.813849,
-31.9319
],
[
115.81386,
-31.9319
],
[
115.81386,
-31.931868
],
[
115.813984,
-31.931868
],
[
115.813984,
-31.931967
],
[
115.81391,
-31.931967
]
]
]
]
}
mapboxgl.accessToken = 'pk.eyJ1IjoiYWxleGdsZWl0aCIsImEiOiIwZU0xU2RZIn0.z4BFqvJIf6fnzlIGQUmptQ';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [115.813867, -31.932177],
zoom: 17
});
map.on('load', function () {
map.addLayer({
'id': 'maine',
'type': 'fill',
'source': {
'type': 'geoj,
'paint': {
'fill-color': '#189',
'fill-opacity': 0.8
}
});
});
You could exploit a data expression.
Here is an example - https://docs.mapbox.com/mapbox-gl-js/example/data-driven-lines/
In your case it would look like this
let data = {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'properties': {
'color': 'red'
},
'geometry': {
'type': 'Polygon',
'coordinates': [
[
[
115.813867,
-31.932177
],
[
115.813867,
-31.932177
],
[
115.813867,
-31.932087
],
[
115.813962,
-31.932087
],
[
115.813962,
-31.932124
],
[
115.814005,
-31.932124
],
[
115.814005,
-31.932168
],
[
115.813962,
-31.932168
],
[
115.813962,
-31.932177
],
[
115.813867,
-31.932177
]
]
]
}
}, {
'type': 'Feature',
'properties': {
'color': 'blue'
},
'geometry': {
'type': 'Polygon',
'coordinates': [
[
[
115.813962,
-31.932087
],
[
115.813894,
-31.932087
],
[
115.813894,
-31.932042
],
[
115.81391,
-31.932042
],
[
115.81391,
-31.931967
],
[
115.813984,
-31.931967
],
[
115.813984,
-31.932042
],
[
115.81401,
-31.932042
],
[
115.81401,
-31.932087
],
[
115.813962,
-31.932087
]
]
]
}
}, {
'type': 'Feature',
'properties': {
'color': 'green'
},
'geometry': {
'type': 'Polygon',
'coordinates': [
[
[
115.81391,
-31.931967
],
[
115.81391,
-31.931931
],
[
115.813849,
-31.931931
],
[
115.813849,
-31.9319
],
[
115.81386,
-31.9319
],
[
115.81386,
-31.931868
],
[
115.813984,
-31.931868
],
[
115.813984,
-31.931967
],
[
115.81391,
-31.931967
]
]
]
}
}]
};
map.on('load', function () {
map.addLayer({
'id': 'maine',
'type': 'fill',
'source': {
'type': 'geojson',
'data': data
},
'layout': {},
'paint': {
'fill-color': ['get', 'color'],
'fill-opacity': 0.8
}
});
});

How can I extend the x-axis on a Highcharts graph?

I have a Highcharts bar graph. Each point has a group of results, however the first and last element are being cropped. How can I extend the x-axis so every bar is shown?
In the image below each group has the same results so you can see the N and P are dropped from the first group and S and Mg from the last grouping.
The data is coming from a database, so i don't know how many groups there will be, or what range (so simply adding a day to each end is not sufficient, the overlap could be larger or smaller)
const conf = {
chart: {
type: "column",
animation: false,
marginRight: 10,
dateFormat: "dd/mm/YYYY"
},
title: {
text: "Spread Events"
},
xAxis: {
type: "datetime",
tickPixelInterval: 50
},
yAxis: {
title: {
text: "Spread"
},
plotLines: [
{
value: 0,
width: 1,
color: "#808080"
}
]
},
legend: {
enabled: true
},
exporting: {
enabled: false
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: this.state.graphData
};
and this is the graphData from the example
[
{
"name": "N",
"data": [[1559669642443, 300], [1559343600000, 300], [1559257200000, 300]]
},
{
"name": "P",
"data": [[1559669642443, 160], [1559343600000, 160], [1559257200000, 160]]
},
{
"name": "K",
"data": [[1559669642443, 470], [1559343600000, 470], [1559257200000, 470]]
},
{
"name": "S",
"data": [[1559669642443, 120], [1559343600000, 120], [1559257200000, 120]]
},
{
"name": "Mg",
"data": [[1559669642443, 90], [1559343600000, 90], [1559257200000, 90]]
}
]
You have Highcharts error #15 in a console, which means that your data is not sorted. Highcharts requires sorted data in ascending X order:
series: [{
...,
data: [
[1559257200000, 300],
[1559343600000, 300],
[1559669642443, 300]
]
}, ...
]
Live demo: http://jsfiddle.net/BlackLabel/y2rzd65m/