Here I load the JSON file and plot them as network graph to visualize relationship between entity. The data has around 60 relationships and I plotted successfully with JavaScript code as follows:
fetch('data.json')
.then((response) => response.json())
.then((jsonData) => {
const dataSample = JSON.parse(jsonData);
const nodes = dataSample.relation.map((relation) => ({
id: relation.target_relation,
relation_type: relation.relation_type,
}));
nodes.push({
id: dataSample.party_source,
relation_type: '-',
});
const edges = dataSample.relation.map((relation) => ({
from: dataSample.party_source,
to: relation.target_relation,
relation_type: relation.relation_type,
}));
// graph data
const data = {
nodes,
edges,
};
const chart = anychart.graph(data);
// node configuration
const configNodes = chart.nodes();
configNodes.normal().height(20);
configNodes.hovered().height(25);
configNodes.tooltip().useHtml(true);
configNodes.tooltip().format(`Party ID: {%id}`);
// edge configuration
const configEdges = chart.edges();
configEdges.labels().enabled(true);
configEdges.labels().format('{%relation_type}');
configEdges.labels().fontSize(12);
configEdges.labels().fontColor('black');
configEdges.labels().fontWeight(500);
configEdges.tooltip().useHtml(true);
configEdges
.tooltip()
.format(`Party Source: {%from}<br>Party Target: {%to}`);
configEdges.arrows({
enabled: true,
size: 8,
});
configEdges.stroke({
color: '#7998FF',
thickness: '1.5',
});
chart.listen('mouseOver', function (e) {
// change the cursor style
document.body.style.cursor = 'pointer';
});
chart.listen('mouseOut', function (e) {
// set the default cursor style
document.body.style.cursor = 'auto';
});
// chart behaviour
chart.container('container');
chart.draw();
});
Unfortunately, I got each node on the network graph overlapped or not properly separated between nodes like picture below:
How to add spacing between nodes in order to avoid the overlapping, I have been searching on the documentation for the network graph, but not found any API function to perform that. Is it supposed to be a small sized data to produce a proper network graph?
Looking at their examples in this playground they are using this to influence the layout. Have you tried playing around with the iterationcount?
// set chart layout settings
chart.layout({ iterationCount: 0 });
Source
https://playground.anychart.com/gallery/src/Network_Graph/Radial_Graph
Related
I am trying get Isochrone contours when the user clicks on a Marker,
The official Mapbox Documentation uses the built in Mapbox JS methods but I can't make it work with Leaflet JS
Here's what I have
function markerOnClick(lon, lat) {
const urlBase = "https://api.mapbox.com/isochrone/v1/mapbox/";
const profile = "cycling"; // Set the default routing profile
const minutes = 10; // Set the default duration
// Create a function that sets up the Isochrone API query then makes an fetch call
async function getIso() {
const query = await fetch(
`${urlBase}${profile}/${lon},${lat}?contours_minutes=${minutes}&polygons=true&access_token=${accessToken}`,
{ method: "GET" }
);
const data = await query.json();
map.getSource("iso").setData(data);
}
// From the official documentation
map.addSource("iso", {
type: "geojson",
data: {
type: "FeatureCollection",
features: [],
},
});
// I have tried to use the Leaflet geoJSON method to add it to the map
L.geoJSON("iso", {
type: "geojson",
data: {
type: "FeatureCollection",
features: [],
},
}).addTo(map);
// Can't find the substitute for this method in Leaflet
map.addLayer(
{
id: "isoLayer",
type: "fill",
// Use "iso" as the data source for this layer
source: "iso",
layout: {},
paint: {
// The fill color for the layer is set to a light purple
"fill-color": "#5a3fc0",
"fill-opacity": 0.3,
},
},
"poi-label"
);
// Make the API call
getIso();
}
I have tried to use the Leaflet method of adding GeoJSON to the map i.e. L.geoJSON but to no avail the mapbox GL JS methods I am trying to replace are
map.addLayer
map.addSource
any advice would be appreciated
L.geoJSON() expects a GeoJSON data structure, not a string. Do read https://leafletjs.com/reference#geojson .
For your particular case you probably want to do something like
const query = await fetch(`${urlBase}${profile}/${lon},${lat}?contours_minutes=${minutes}&polygons=true&access_token=${accessToken}`,{ method: "GET" });
const data = await query.json();
L.geoJson(data).addTo(map);
I am working from this update feature in realtime from Mapbox GL JS (https://docs.mapbox.com/mapbox-gl-js/example/live-update-feature/) and have successfully recreated it in my react app (see large code block below). Context: I am animating a bike route on my map.
What I am trying to accomplish: I would like to have a leading beacon (circle) to better show position as the line is animated.
What I have attempted: I successfully adding a second layer that is a circle but it adds [or plots] a circle for each data point along the entirety of the route.
Here is that specific Layer code (you will also see it in the full code below):
map.addLayer({
'id': 'beacon',
'type': 'circle',
'source': 'trace',
'paint': {
'circle-radius': 5,
'circle-color': 'white',
'circle-stroke-color': 'black',
'circle-stroke-width': 0.5
}
});
My questions:
Is adding the circle element as a Layer the first step?
How or where in the code can I state that only the leading data point should be plotted as a circle?
If there is a common javascript function/method to accomplish this, could you please provide the correct language for it so I can continue to research with more effective results?
My code is basically the same as is used in the example provided. Again, This code works but it does not achieve my goal of only showing the leading circle -- rather it shows all plotted data points as a circle.
const Component = () => {
const mapContainerRef= useRef(null);
useEffect(() => {
const map = new mapboxgl.Map({
container: mapContainerRef.current,
style: 'mapbox://styles/mapbox/satellite-v9',
projection: 'mercator'
});
map.on('load', async () => {
// We fetch the JSON here so that we can parse and use it separately
// from GL JS's use in the added source.
const response = await fetch("../mydata.geojson");
//parsing the json
const data = await response.json();
// save full coordinate list for later
const coordinates = data.features[0].geometry.coordinates;
console.log(coordinates);
// start by showing just the first coordinate
data.features[0].geometry.coordinates = [coordinates[0]];
// add it to the map
map.addSource('trace', { type: 'geojson', data: data });
map.addLayer({
'id': 'trace',
'type': 'line',
'source': 'trace',
'paint': {
'line-color': '#000000',//'#F55DE1',
'line-opacity': 0.5,
'line-width': 8
}
});
map.addLayer({
'id': 'beacon',
'type': 'circle',
'source': 'trace',
'paint': {
'circle-radius': 5,
'circle-color': 'white',
'circle-stroke-color': 'black',
'circle-stroke-width': 0.5
}
});
// setup the viewport
map.jumpTo({
'center': coordinates[0],
'zoom': 14
});
//map.setPitch(30);
// on a regular basis, add more coordinates from the saved list and update the map
let i = 0;
console.log(coordinates.length);
const timer = setInterval(() => {
if (i < coordinates.length) {
data.features[0].geometry.coordinates.push(coordinates[i]);
map.getSource('trace').setData(data);
map.panTo(coordinates[i]); //pans to each coordinate area
i++;
} else {
window.clearInterval(timer);
}
}, 17.5);
});
return () => map.remove();
}, []);
return (
<div className="map-container" ref={mapContainerRef} style={{width:'100%', height:'100vh'}}/>
)
};
export default Component;
Edited several times to enhance post context.
I am using Chart.js 3.5 and Vue 3.
I was successfully able to draw a chart, and I am trying to trigger a data change, inside a Vue method. Unfortunately, I encounter the following issue: "Uncaught TypeError: Cannot set property 'fullSize' of undefined".
Edit2: Added a missed }. Code should now be runnable
MyChart.vue:
<template>
<canvas id="chartEl" width="400" height="400" ref="chartEl"></canvas>
<button #click="addData">Update Chart</button>
</template>
<script>
import Chart from 'chart.js/auto';
export default {
name: "Raw",
data() {
return {
chart: null
}
},
methods: {
createChart() {
this.chart= new Chart(this.$refs["chartEl"], {
type: 'doughnut',
data: {
labels: ['VueJs', 'EmberJs', 'ReactJs', 'AngularJs'],
datasets: [
{
backgroundColor: [
'#41B883',
'#E46651',
'#00D8FF',
'#DD1B16'
],
data: [100, 20, 80, 20]
}
]
},
options: {
plugins: {}
}
})
},
addData() {
const data = this.chart.data;
if (data.datasets.length > 0) {
data.labels.push('data #' + (data.labels.length + 1));
for (var index = 0; index < data.datasets.length; ++index) {
data.datasets[index].data.push(123);
}
// Edit2: added missed }
this.chart.update(); } // this line seems to cause the error}
}
},
mounted () {
this.createChart()
},
}
</script>
Edit1: Adding the following to the options makes the chart update successfully, but the error is still present and the animation does not work. The chart flickers and displays the final (updated) state. Other animations, such as hiding/showing arcs do not seem to be afected
options: {
responsive: true,
}
Edit3: Adding "maintainAspectRatio:false" option seems to again stop chart from updating (the above mentioned error is still present)
By walking through the debugger, the following function from 'chart.esm.js' seems to be called successfully a few times, and then error out on last call:
beforeUpdate(chart, _args, options) {
const title = map.get(chart); // this returns null, which will cause the next call to error with the above mentioned exception.
layouts.configure(chart, title, options);
title.options = options;
},
//////////////////////
configure(chart, item, options) {
item.fullSize = options.fullSize;
item.position = options.position;
item.weight = options.weight;
},
This may be a stale post but I just spent several hours wrestling with what seems like the same problem. Perhaps this will help you and/or future people with this issue:
Before assigning the Chart object as an attribute of your Vue component, call Object.seal(...) on it.
Eg:
const chartObj = new Chart(...);
Object.seal(chartObj);
this.chart = chartObj;
This is what worked for me. Vue aggressively mutates attributes of objects under its purview to add reactivity, and as near as I can tell, this prevents the internals of Chart from recognising those objects to retrieve their configurations from its internal mapping when needed. Object.seal prevents this by barring the object from having any new attributes added to it. I'm counting on Chart having added all the attributes it needs at init time - if I notice any weird behaviour from this I'll update this post.
1 year later, Alan's answer helps me too, but my code failed when calling chart.destroy().
So I searched and found what seems to be the "vue way" of handling it: markRaw, here is an example using options API:
import { markRaw } from 'vue'
// ...
export default {
// ...
beforeUnmount () {
if (this.chart) {
this.chart.destroy()
}
},
methods: {
createChart() {
const chart = new Chart(this.$refs["chartEl"], {
// ... your chart data and options
})
this.chart = markRaw(chart)
},
addData() {
// ... your update
this.chart.update()
},
},
}
I am trying to utilize the library #gitgraph/js in my application (Note: I cannot use the React or NodeJS version, only the plain JS):
https://github.com/nicoespeon/gitgraph.js/tree/master/packages/gitgraph-js
Here is an example of what I am trying to do:
https://jsfiddle.net/Ben_Vins/fwcah5s0/7/
var myTemplateConfig = {
// … any specific template configuration
commit: {
shouldDisplayTooltipsInCompactMode: true, // default = true
tooltipHTMLFormatter: function(commit) {
return "<b>BV" + commit.sha1 + "</b>" + ": " + commit.message;
},
}
};
// Instantiate the graph.
const gitgraph = GitgraphJS.createGitgraph(graphContainer, {
mode: GitgraphJS.Mode.Compact,
template: new GitgraphJS.templateExtend(GitgraphJS.TemplateName.Metro, myTemplateConfig ),
});
// Simulate git commands with Gitgraph API.
const master = gitgraph.branch("master");
master.commit("Initial commit");
const develop = gitgraph.branch("develop");
develop.commit("Add TypeScript");
const aFeature = gitgraph.branch("a-feature");
aFeature
.commit("Make it work")
.commit({ subject: "Make it right", hash: "test" })
.commit("Make it fast");
develop.merge(aFeature);
develop.commit("Prepare v1");
master.merge(develop).tag("v1.0.0");
By default, the result were too big, so I have applied a css to scale down the graph (the graph is an inline SVG without a cropbox property, so this is the only trick I could find).
What I would like to do:
Customize the tooltip of the onhover of the commits node (making it larger, change the text, change its css if possible)
Add a onclick event to capture the commit (in particular the commit hash to be used elsewhere in my application)
Extra points:
The documentation is limited and the examples from
https://github.com/nicoespeon/gitgraph.js/tree/master/packages/stories/src/gitgraph-js/
are in typescript. Are they also applicable for the JS version of gitgraph-js?
Note that the documentation of gitgraph.js seemed more detailed i.e. https://github.com/nicoespeon/gitgraph.js/blob/master/packages/gitgraph-js/MIGRATE_FROM_GITGRAPH.JS.md but I was trying to use the next version i.e. #gitgraph/js
Thanks for reaching out. I can add more colors to this as the author of the lib.
To customize the tooltip of a commit, you can provide a renderTooltip() custom function. It will give you the reference to the commit object, so you can customize at your will.
Same, you can pass a onClick() function that will give you the reference to the commit object
The examples are in TypeScript, but that's valid JS if you simply remove the types. Also, the gitgraph-js stories looks like they are React code, but they're not. They're simply wrapped in a React component so we could run them in Storybook along with the gitgraph-react ones.
With the latest version of the lib, you could try the following:
const graphContainer = document.getElementById("graph-container");
// Instantiate the graph.
const withoutBranchLabels = GitgraphJS.templateExtend(GitgraphJS.TemplateName.Metro, {
branch: { label: { display: false } },
});
const gitgraph = GitgraphJS.createGitgraph(graphContainer, {
mode: GitgraphJS.Mode.Compact,
template: withoutBranchLabels,
});
// Simulate git commands with Gitgraph API.
let storedCommit;
gitgraph
.commit({
subject: "Initial commit",
onClick: (commit) => storedCommit = commit
})
.commit({
subject: "Another commit",
onClick: (commit) => storedCommit = commit
})
.commit({
subject: "Do something crazy",
renderTooltip,
onClick: (commit) => storedCommit = commit
});
gitgraph
.branch("dev")
.commit({
subject: "Oh my god",
renderTooltip,
})
.commit({
subject: "This is a saxo!",
renderTooltip,
});
// Logs in console the sha1 of the clicked commit every 3s (for master branch only)
setInterval(() => storedCommit && console.log(`stored commit sha1: ${storedCommit.hashAbbrev}`), 3000)
// Custom tooltip renderer
function renderTooltip (commit) {
const commitSize = commit.style.dot.size * 2;
return createG({
translate: { x: commitSize + 10, y: commitSize / 2 },
children: [
createText({
fill: commit.style.dot.color,
content: `BV${commit.hashAbbrev}: ${commit.subject}`
})
],
});
}
// Helper functions to create SVGs
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
function createText(options) {
const text = document.createElementNS(SVG_NAMESPACE, "text");
text.setAttribute("alignment-baseline", "central");
text.setAttribute("dominant-baseline", "central");
text.textContent = options.content;
if (options.bold) {
text.setAttribute("font-weight", "bold");
}
if (options.fill) {
text.setAttribute("fill", options.fill);
}
if (options.font) {
text.setAttribute("style", `font: ${options.font}`);
}
if (options.anchor) {
text.setAttribute("text-anchor", options.anchor);
}
if (options.translate) {
text.setAttribute("x", options.translate.x.toString());
text.setAttribute("y", options.translate.y.toString());
}
if (options.onClick) {
text.addEventListener("click", options.onClick);
}
return text;
}
function createG(options) {
const g = document.createElementNS(SVG_NAMESPACE, "g");
options.children.forEach((child) => child && g.appendChild(child));
if (options.translate) {
g.setAttribute(
"transform",
`translate(${options.translate.x}, ${options.translate.y})`,
);
}
if (options.fill) {
g.setAttribute("fill", options.fill);
}
if (options.stroke) {
g.setAttribute("stroke", options.stroke);
}
if (options.strokeWidth) {
g.setAttribute("stroke-width", options.strokeWidth.toString());
}
if (options.onClick) {
g.addEventListener("click", options.onClick);
}
if (options.onMouseOver) {
g.addEventListener("mouseover", options.onMouseOver);
}
if (options.onMouseOut) {
g.addEventListener("mouseout", options.onMouseOut);
}
return g;
}
You can't just return HTML though, it must be SVG because the current renderer only handles SVG. That is surely less convenient than before, thus I encourage you to build helper functions like I did here. You can find helpers used in the stories too.
I hope that will be helpful. You can play with the new online playground too: https://codepen.io/nicoespeon/pen/arqPWb?editors=1010
Finally, I'm not maintaining the library much and I'm still looking for active maintainers: https://github.com/nicoespeon/gitgraph.js/issues/328
We want to show loading icon when the data loads and chart is made in highcharts.
Below is pseudo code:
// service call
// data is pushed in data set
// and that data is used in highcharts.
$('Chart_name').highcharts({
});
Render chart without data, then show loading screen, fetch data, hide loading screen.
Example:
// Options without data
const options = {
series: [{
data: [],
type: 'column'
}]
}
// Redner chart
const chart = Highcharts.chart('container', options)
// Simulate fetch request timeout, get data after some delay
setTimeout(() => {
const data = [1,2,3]
chart.hideLoading()
chart.series[0].setData(data)
}, 2000)
// Show loading screen
chart.showLoading()
Live example:
https://jsfiddle.net/hLj1advd/