I am looking for an algorithm to make a pathfinding tool. I believe, it is not exact salesman problem due to different nature of desired result.
Main goal is to build route through multiple points to visit more points in less time. It does not require to include all points into route, just opposite: if visiting a point or a group of points makes big detour, it is not worth including into route, as point/distance ratio would drop significantly.
Conditions:
there will always be starting point A (chosen by user), but finish can be back in point A, can be in point B or can be undefined to end route when visiting any more points would decrease efficiency
total pool of points (chosen by user) ranges from tens to thousands depending on filter
points can have weights to define how much visiting them contributes
app has to be more or less real time: user needs to get result within few minutes from entering starting conditions
To decrease waiting times I can make two optimizations: merge nearby points by distance into one point with increased weight and limit visitable points by certain radius from A or AB vector.
Bruteforce method does not work with big numbers, and popular pathfinding algorithm A* does not seem to be applicable here as well from what I understood. I thought about using ant algorithm or looking into other genetic algorithms, but is there any existing algorithm/method specifically made for such task? Also what (if any) further optimizations could be made to the pool of points for better results?
My approach so far (JavaScript). Scenarios 2 and 3 include distance to finish point as well.
let maxRatio = 0
let bestRoute = []
for (let path of allPaths) {
let distance = 0
let weight = 0
let currentPosition = {x: 0, y: 0}
for (let point of path) {
distance += getDistance(currentPosition, point)
weight += point.weight
currentPosition.x = point.x
currentPosition.y = point.y
}
if (weight / distance > maxRatio) {
maxRatio = weight / distance
bestRoute = path
}
}
With 2 sets of starting conditions (difference only in last point)
// input 1
[
{x: 10, y: 12, weight: 1.2},
{x: 15, y: 14, weight: 1},
{x: 6, y: 8, weight: 1},
{x: 30, y: 30, weight: 2},
]
// input 2
[
{x: 10, y: 12, weight: 1.2},
{x: 15, y: 14, weight: 1},
{x: 6, y: 8, weight: 1},
{x: 30, y: 30, weight: 4},
]
Scenario 1, from point A (0:0) to the end of route:
// output 1.1
Best weight/distance ratio: 0.15207666105559417
at visiting order [
{ x: 0, y: 0 },
{ x: 6, y: 8, weight: 1 },
{ x: 10, y: 12, weight: 1.2 },
{ x: 15, y: 14, weight: 1 }
]
Here we can see that one point, 30:30 with weight 2, is not included into the best route because it is simply too far away.
// output 1.2
Best weight/distance ratio: 0.16754421339617698
at visiting order [
{ x: 0, y: 0 },
{ x: 6, y: 8, weight: 1 },
{ x: 10, y: 12, weight: 1.2 },
{ x: 15, y: 14, weight: 1 },
{ x: 30, y: 30, weight: 4 }
]
Here we can see that point, 30:30 with weight 4 now, being included into the best route because visiting it benefits us.
Scenario 2, from point A (0:0) back to point A (0:0):
// output 2.1
Best weight/distance ratio: 0.07699655016791247
at visiting order [
{ x: 0, y: 0 },
{ x: 15, y: 14, weight: 1 },
{ x: 10, y: 12, weight: 1.2 },
{ x: 6, y: 8, weight: 1 },
{ x: 0, y: 0 }
]
Different order or points this time, but still furthest point not included
// output 2.2
Best weight/distance ratio: 0.08469183439702697
at visiting order [
{ x: 0, y: 0 },
{ x: 15, y: 14, weight: 1 },
{ x: 30, y: 30, weight: 4 },
{ x: 10, y: 12, weight: 1.2 },
{ x: 6, y: 8, weight: 1 },
{ x: 0, y: 0 }
]
And here, with weight 4, we include it.
Scenario 3, from point A (0:0) to point B (10:10):
// output 3.1
Best weight/distance ratio: 0.12459750581354255
at visiting order [
{ x: 0, y: 0 },
{ x: 6, y: 8, weight: 1 },
{ x: 10, y: 12, weight: 1.2 },
{ x: 10, y: 10 }
]
Here optimal route became shorter
// output 3.2
Best weight/distance ratio: 0.12459750581354255
at visiting order [
{ x: 0, y: 0 },
{ x: 6, y: 8, weight: 1 },
{ x: 10, y: 12, weight: 1.2 },
{ x: 10, y: 10 }
]
But even with weight 4 on (30:30) point we do not include it.
Related
PROBLEM STATEMENT
Hello Guys. I am new to Web Development. I am working on a project that involves showing uptime charts of certain websites for website monitoring. The kind of chart needed is as follows:
Uptime Chart
As we can see, it's a time series chart and in the same chart the successful calls to the site are in green and the unsuccessful ones are shown in red. My problem is I am unable to get this in the same chart. So can someone tell me what has to be done ?
I am currently using Chart.js for showing realtime charts on the webpage.
Methods Tried
Let's say my time series dummy dataset looks like this:
data: [
{ x: "2021-03-08 00:08:46", y: 1 },
{ x: "2021-03-08 00:38:46", y: 0 },
{ x: "2021-03-08 01:08:46", y: 1 },
{ x: "2021-03-08 01:38:46", y: 0 },
{ x: "2021-03-08 02:08:46", y: 1 },
{ x: "2021-03-08 02:38:46", y: 0 },
{ x: "2021-03-08 03:08:46", y: 1 },
{ x: "2021-03-08 03:38:46", y: 0 },
{ x: "2021-03-08 04:08:46", y: 1 },
{ x: "2021-03-08 04:38:46", y: 0 },
{ x: "2021-03-08 05:08:46", y: 1 },
{ x: "2021-03-08 05:38:46", y: 0 },
{ x: "2021-03-08 06:08:46", y: 1 },
{ x: "2021-03-08 06:38:46", y: 0 },
{ x: "2021-03-08 07:08:46", y: 1 },
{ x: "2021-03-08 07:38:46", y: 0 },
{ x: "2021-03-08 08:08:46", y: 1 },
{ x: "2021-03-08 08:38:46", y: 0 },
{ x: "2021-03-08 09:08:46", y: 1 },
{ x: "2021-03-08 09:38:46", y: 0 },
{ x: "2021-03-08 10:08:46", y: 1 },
{ x: "2021-03-08 10:38:46", y: 0 }
]
What I tried is separating the 1's and 0's and in the same chart showing the 1's in green and 0's in red having value as 1. My code from chart.js looks like this:
var ctx = document.getElementById("myChart");
ctx.height = 500;
ctx.width = 500;
var data = {
datasets: [
{
fill: true,
label: "Completed",
borderColor: "#4BB543",
backgroundColor: "#4BB543",
borderColor: "#4BB543",
data: [
{ x: "2021-03-08 00:08:46", y: 1 },
{ x: "2021-03-08 01:08:46", y: 1 },
{ x: "2021-03-08 02:08:46", y: 1 },
{ x: "2021-03-08 03:08:46", y: 1 },
{ x: "2021-03-08 04:08:46", y: 1 },
{ x: "2021-03-08 05:08:46", y: 1 },
{ x: "2021-03-08 06:08:46", y: 1 },
{ x: "2021-03-08 07:08:46", y: 1 },
{ x: "2021-03-08 08:08:46", y: 1 },
{ x: "2021-03-08 09:08:46", y: 1 },
{ x: "2021-03-08 10:08:46", y: 1 },
],
},
{
fill: true,
label: "Issues",
borderColor: dangerColor,
backgroundColor: "#FF0000",
borderColor: "#FF0000",
data: [
{ x: "2021-03-08 00:38:46", y: 1 },
{ x: "2021-03-08 01:38:46", y: 1 },
{ x: "2021-03-08 02:38:46", y: 1 },
{ x: "2021-03-08 03:38:46", y: 1 },
{ x: "2021-03-08 04:38:46", y: 1 },
{ x: "2021-03-08 05:38:46", y: 1 },
{ x: "2021-03-08 06:38:46", y: 1 },
{ x: "2021-03-08 07:38:46", y: 1 },
{ x: "2021-03-08 08:38:46", y: 1 },
{ x: "2021-03-08 09:38:46", y: 1 },
{ x: "2021-03-08 10:38:46", y: 1 },
],
},
],
};
var lineChart = new Chart(ctx, {
type: "line",
data: data,
options: {
scales: {
xAxes: [
{
type: "time",
time: {
unit: "hour",
},
display: true,
scaleLabel: {
display: true,
labelString: "Time",
},
ticks: {
major: {
fontStyle: "bold",
fontColor: "#FF0000",
},
},
},
],
yAxes: [
{
display: true,
scaleLabel: {
display: true,
labelString: "Response Time(s)",
},
ticks: {
beginAtZero: true,
},
},
],
},
maintainAspectRatio: false,
bezierCurve: false,
},
});
Here's how my output chart looks like:
Obtained Chart using the code
As we can see the green is overpowering the red. I tried with other shades of green but the results are still not good. Please take a look and let me know how can make the uptime chart as the one shown in the first image. Thanks.
Can be done using visavail.js library. From the description:
The Visavail.js chart allows a quick insight into which periods of time a time-dependent dataset covers. It is visually similar to a Gantt chart and allows easy identification of missing pieces and gaps in large datasets. Missing periods of data are marked in red while blocks of complete periods of data are marked in green. The user discovers dates that define start and end of such periods by tooltips, as shown in the picture below.
[...] The Visavail.js library takes single data points with dates and information about data availability as inputs, combines them into time blocks, and visualizes these blocks.
The dataset should be in the following format:
var dataset = [{
"measure": "Series", // name of the data series, will become y-axis label
"measure_description": "Description", // description of y-axis label, visible with mouse over
"interval_s": 1800, // time period in seconds a single data point is expected to cover (30 minutes in your example)
"data": [
["2021-03-08 00:08:46", 1],// data as arrays of period start data string and bit determining
["2021-03-08 00:38:46", 0],
["2021-03-08 01:08:46", 1],
["2021-03-08 01:38:46", 0],
["2021-03-08 02:08:46", 1],
["2021-03-08 02:38:46", 0],
["2021-03-08 03:08:46", 1],
["2021-03-08 03:38:46", 0],
["2021-03-08 04:08:46", 1],
["2021-03-08 04:38:46", 0],
["2021-03-08 05:08:46", 1],
["2021-03-08 05:38:46", 0],
["2021-03-08 06:08:46", 1],
["2021-03-08 06:38:46", 0],
["2021-03-08 07:08:46", 1],
["2021-03-08 07:38:46", 0],
["2021-03-08 08:08:46", 1],
["2021-03-08 08:38:46", 0],
["2021-03-08 09:08:46", 1],
["2021-03-08 09:38:46", 0],
["2021-03-08 10:08:46", 1],
["2021-03-08 10:38:46", 0]
]
}];
In the following links you can see several examples of how to use the library CodeSandbox Demo.
I am using LightningChart JS and would like to implement a virtual measurement device, where I can click on point A and then drag to point B and obtain the x,y values of both point A and point B.
As far as I have looked into the event handlers they just return a mouse event with start and stop positions in terms of screen positions. Please, correct me if I'm wrong. Also please suggest an efficient way to do this.
Thanks in advance.
The mouse events return the mouse coordinates in the same coordinate space as normal JS mouse events. To get the click location in the series coordinate space, a couple of steps need to be taken.
First the mouse coordinates need to be converted to the engine coordinate space. The engine coordinate space is the canvas area with 0,0 on the bottom left of the canvas. This can be done with chart.engine.clientLocation2Engine(ev.clientX,ev.clientY). This returns the event coordinate in the engine coordinate space using the chart engine scale.
This needs to be then converted to the series coordinate. This can be done with translatePoint method. translatePoint can be used to convert points between two different scales. Scale in LightningChart JS is basically a coordinate space.
const m = chart.engine.clientLocation2Engine(ev.clientX, ev.clientY)
const translated = translatePoint(m, chart.engine.scale, lineSeries.scale)
Now the translated variable contains the click location in the series coordinate space.
See a full code snippet below where you can drag on the series area and when drag is stopped markers are placed to the start and end locations of the drag.
const {
lightningChart,
SolidLine,
SolidFill,
ColorRGBA,
AxisTickStrategies,
UIOrigins,
DataPatterns,
translatePoint,
ColorHEX
} = lcjs
const chart = lightningChart().ChartXY()
const diesel = [
{ x: 0, y: 1.52 },
{ x: 1, y: 1.52 },
{ x: 2, y: 1.52 },
{ x: 3, y: 1.58 },
{ x: 4, y: 2.00 },
{ x: 5, y: 2.00 },
{ x: 6, y: 2.00 },
{ x: 7, y: 2.00 },
{ x: 8, y: 2.26 },
{ x: 9, y: 1.90 },
{ x: 10, y: 1.90 },
{ x: 11, y: 1.90 },
{ x: 12, y: 1.90 },
{ x: 13, y: 1.60 },
{ x: 14, y: 1.60 },
{ x: 15, y: 1.60 },
{ x: 16, y: 1.00 },
{ x: 17, y: 1.00 },
{ x: 18, y: 1.00 },
{ x: 19, y: 1.74 },
{ x: 20, y: 1.47 },
{ x: 21, y: 1.47 },
{ x: 22, y: 1.47 },
{ x: 23, y: 1.74 },
{ x: 24, y: 1.74 },
{ x: 25, y: 1.74 },
{ x: 27, y: 1.5 },
{ x: 28, y: 1.5 },
{ x: 29, y: 1.5 }
]
const gasoline = [
{ x: 0, y: 1.35 },
{ x: 1, y: 1.35 },
{ x: 2, y: 1.35 },
{ x: 3, y: 1.35 },
{ x: 4, y: 1.90 },
{ x: 5, y: 1.90 },
{ x: 6, y: 1.90 },
{ x: 7, y: 1.92 },
{ x: 8, y: 1.50 },
{ x: 9, y: 1.50 },
{ x: 10, y: 1.3 },
{ x: 11, y: 1.3 },
{ x: 12, y: 1.3 },
{ x: 13, y: 1.3 },
{ x: 14, y: 1.3 },
{ x: 15, y: 1.32 },
{ x: 16, y: 1.40 },
{ x: 17, y: 1.44 },
{ x: 18, y: 1.02 },
{ x: 19, y: 1.02 },
{ x: 20, y: 1.02 },
{ x: 21, y: 1.02 },
{ x: 22, y: 1.02 },
{ x: 23, y: 1.02 },
{ x: 24, y: 1.02 },
{ x: 25, y: 1.02 },
{ x: 27, y: 1.30 },
{ x: 28, y: 1.30 },
{ x: 29, y: 1.30 }
]
const lineSeries = chart.addLineSeries({ dataPattern: DataPatterns.horizontalProgressive })
const lineSeries2 = chart.addLineSeries({ dataPattern: DataPatterns.horizontalProgressive })
lineSeries2.add(diesel.map((point) => ({ x: point.x, y: point.y })))
lineSeries.add(gasoline.map((point) => ({ x: point.x, y: point.y })))
const markerA = chart.addChartMarkerXY()
.setPointMarker((marker) => marker.setFillStyle((f => f.setColor(ColorHEX('#f00')))))
.setMouseInteractions(false)
const markerB = chart.addChartMarkerXY()
.setPointMarker((marker) => marker.setFillStyle((f => f.setColor(ColorHEX('#0f0')))))
.setMouseInteractions(false)
function getClickInSeriesScale(point, scale) {
const m = chart.engine.clientLocation2Engine(point.x, point.y)
return translatePoint(m, chart.engine.scale, scale)
}
chart.onSeriesBackgroundMouseDragStop((obj, ev, b, start) => {
let pointA = getClickInSeriesScale(start, lineSeries.scale)
let pointB = getClickInSeriesScale({x:ev.clientX,y:ev.clientY}, lineSeries.scale)
// move markes to start and end points
markerA.setPosition(pointA)
markerB.setPosition(pointB)
})
<script src="https://unpkg.com/#arction/lcjs#1.3.1/dist/lcjs.iife.js"></script>
I have tried to show 2 series of data in Area chart | Highcharts. Its working fine but there is a whitespace between the series. How to remove the space?
Fiddle : http://jsfiddle.net/alagarrk/0jcg5047/1/
series: [{
name: 'Educated people',
data: [{
x: 0,
y: 20000
}, {
x: 1,
y: 19000
}, {
x: 2,
y: 19000
}, {
x: 3,
y: 18000
}, {
x: 4,
y: 17000
}, {
x: 5,
y: 16000
}, {
x: 6,
y: 15000
}, {
x: 7,
y: 14000
}]
}, {
name: 'Uneducated people',
data: [{
x: 8,
y: 13000
}, {
x: 9,
y: 12000
}, {
x: 10,
y: 11000
}, {
x: 11,
y: 10000
}]
}]
Please check and guide me to fix this issue
There is no "fixing" this issue, you don't have any data between point 7 and 8. What you can do is start series 2 (uneducated people) on point 7. That way it will look connected.
name: 'Uneducated people',
data: [{
x: 7,
y: 13000
}, {
x: 8,
y: 12000
...
Working example: http://jsfiddle.net/0jcg5047/2/
Is there a way to plot 2 graphs together with different Y axes?
I have some data (page views, stacked area by referrer) which I'd like to plot with an overlayed line graph of number of actions over time.
I already have both graphs separately, but just wondering if there's a way to combine them into 1.
You should be able to draw both graphs by passing in the element you're outputting to as the target for each. Are you using SVG for output? Can you post some of the code you're working with?
Sure.
Here's the simplest of examples.
var graph = new Rickshaw.Graph({
element: document.getElementById("chart"),
renderer: 'multi',
series: [{
name: 'one',
data: [{
x: 0,
y: Math.random()
}, {
x: 1,
y: Math.random()
}, {
x: 2,
y: Math.random()
}, {
x: 3,
y: Math.random()
}, {
x: 4,
y: Math.random()
}, {
x: 5,
y: Math.random()
}],
color: 'blue',
renderer: 'stack'
}, {
name: 'two',
data: [{
x: 0,
y: Math.random()
}, {
x: 1,
y: Math.random()
}, {
x: 2,
y: Math.random()
}, {
x: 3,
y: Math.random()
}, {
x: 4,
y: Math.random()
}, {
x: 5,
y: Math.random()
}],
renderer: 'line',
color: 'red'
}]
});
graph.render();
I am building something where I need to draw multiple rectangles, either adding them or subtracting them, and from that produce one or more polygons that represent the overall area enclosed. This would work like the thing you see in paint programs, etc, where you can create a selection by drawing rectangles and clicking the + or - button to determine whether to add or subtract from the current selected area.
I can do all the drawing code, but need to know how to convert an ordered series of rectangles, each with a mode of "add" or "subtract", into one or more polygons. Here is how I envision it being used:
var rectList = [
{x: 50, y: 40, w: 20, h: 30, mode: "+"},
{x: 24, y: 12, w: 14, h: 62, mode: "+"},
{x: 12, y: 30, w: 34, h: 14, mode: "-"},
{x: 22, y: 21, w: 45, h: 19, mode: "+"},
{x: 17, y: 20, w: 10, h: 21, mode: "+"}
];
var polygonList = getPolygonsFromRectangleList (rectList);
The polygonList might look like this (an array of arrays of points). (The numbers below are just made up and have nothing to do with the input above)
[
[
{x: 23, y: 12},
{x: 14, y: 12},
{x: 14, y: 36},
{x: 24, y: 36},
....
],
[
{x: 32, y: 45},
{x: 32, y: 22},
{x: 14, y: 22},
...
]
I imagine this must be a pretty standard graphics gem sort of thing.