Set axis max to image size
Hi all,
Here's what I'm trying to do: I have an image that I want to show markers on. To do this I'm using a scatter graph widget, where I use plotBackgroundImage to set an image to the background.
In my dataset I have a set of items with x and y coordinates. However I don't have the dimensions of the image itself in the dataset.
What this means right now is that I can easily apply the background image and the dots show on top, but they don't show in the right location of the image as the min and max coordinates for the chart are automatically chosen.
What I'd like is to have the client browser find the height and width of the image that is being displayed in the background, and then apply it to the max x and y values of the chart. In theory this should make all the dots appear in the right location.
Here's my current widget script:
widget.on("beforeviewloaded", (scope, args) => {
args.options.yAxis[0].min = 0;
args.options.xAxis.min = 0
var img = new Image();
img.onload = () => {
args.options.yAxis[0].max = img.height;
args.options.xAxis.max = img.width;
}
img.src = URL_TO_IMAGE
args.options.chart.plotBackgroundImage = URL_TO_IMAGE;
});
-
The above script is a snippet of my full script - the rest of the script executes a JAQL query as the URL of the image file is also stored in the dataset.
In a different widget I created I also had the dimensions of the image itself stored in the dataset, and then it was easy - I just fetched that too, and applied it to the x and y axis.
I'd prefer to avoid having to do that again.
-
Update on this - after much trial and error, I found a way. I don't think it's perfect but it's better than nothing.
I found that you can't just set the x and y axis of a graph to anything - if it can't be cleanly divided into an interval, then it won't be accepted. So instead I take a different approach - I read the current axis limits, create a scaling multiplier, and then change the x and y coordinates of the dataset to scale correctly within the given limits.
To tackle the reading of the image dimensions, instead of trying to wait for the image to load, I let the widget load as normal, then do a widget.redraw() as soon as the dimensions are available.
let refresh = true;
const setDimensions = (url) => {
return new Promise((resolve, reject) => {
var img = new Image();
img.src = url;
img.onload = () => {
const scale = {
x: (widget.style.xAxis.max - widget.style.xAxis.min) / img.naturalWidth,
y: (widget.style.yAxis.max - widget.style.yAxis.min) / img.naturalHeight
};
widget.rawQueryResult.values.forEach((item) => {
item[0].data = item[0].data * scale.x + widget.style.xAxis.min;
item[0].text = item[0].data.toString();
item[1].data = item[1].data * scale.y + widget.style.yAxis.min;
item[1].text = item[1].data.toString();
});
widget.redraw();
resolve();
};
});
};
widget.on("queryend", (scope, result) => {
refresh = result.reason != "widgetredraw";
});
widget.on("beforeviewloaded", (scope, args) => {
args.options.chart.plotBackgroundImage = URL_TO_IMAGE;
if (refresh) {
setDimensions(URL_TO_IMAGE);
}
});The "refresh" variable is there to break an infinite loop - if we redraw the widget to change the scaling multiplier, then "beforeviewloaded" is triggered again. Essentially I just look for the reason that the widget is being refreshed, and set refresh to false if the reason is a redraw.
Would like to hear if anyone has a cleaner solution. I don't like the refresh global variable method, it feels very hacky.
Please sign in to leave a comment.
Comments
2 comments