Reputation: 647
I'm drawing a simple undirected graph using HTML canvas. For drawing nodes I'm using fillRect
as follows:
const MARKER_SIZE = 5;
const canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillRect(Math.floor(pixel.x), Math.floor(pixel.y), MARKER_SIZE, MARKER_SIZE);
And for edges I'm drawing lines as follows:
ctx.beginPath();
ctx.moveTo(fromPixel.x + MARKER_SIZE/2, fromPixel.y + MARKER_SIZE/2);
ctx.lineTo(toPixel.x + MARKER_SIZE/2, toPixel.y + MARKER_SIZE/2);
ctx.stroke();
I want to add text to both nodes and edges. Trivial solution is to add the following for both nodes and edges:
ctx.font = "15px Arial";
ctx.fillText(edge.distance + " km", x, y);
But it does not format well
I would want the California
text to be aligned center relative to the node. And the text of 1km
to be present above the edge. Any idea how can I get a generic solution for this?
I would also want the ability for the users to align those text in a way they want, after the page is rendered in browser. Is it possible to do so?
Note: I'm fairly new to frontend development. Also I'm completely open to any javascript library that makes this kind of plotting easier.
Upvotes: 1
Views: 700
Reputation: 2958
Centering text on a point can be done with textAlign = 'center'
. Once you have it centered you can use translate and rotate to position the text where you want it. The following snippet is very static so if your map is going to be large with lots of points and names you may want to create a function that takes in the points and determines the angle of each line and then passes that angle to the rotate
method.
The angle of a line can be found with atan2
. Once you have that you can rotate the text to the exact angle of the line. Everything below can be written to be more dynamic but gives you an idea of how you can achieve what you want.
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 400;
class Map {
constructor(x, y, name) {
this.x = x;
this.y = y;
this.name = name;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
ctx.fill();
ctx.font = "12px arial";
ctx.textAlign = "center";
ctx.fillText(this.name, this.x, this.y - 10);
}
}
let locations = [];
let california = locations.push(new Map(30, 200, "California"));
let oregon = locations.push(new Map(50, 100, "Oregon"));
let washington = locations.push(new Map(50, 25, "Washington"));
let nevada = locations.push(new Map(150, 180, "Nevada"));
let utah = locations.push(new Map(210, 170, "Utah"));
let arizona = locations.push(new Map(200, 230, "Arizona"));
function drawLocations() {
for (let i = 0; i < locations.length; i++) {
locations[i].draw();
}
}
drawLocations();
class Roads {
constructor(x1, y1, x2, y2, name) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.name = name;
}
draw() {
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.moveTo(this.x1, this.y1);
ctx.lineTo(this.x2, this.y2);
ctx.fill();
ctx.stroke();
ctx.closePath();
}
}
let roads = [];
let CA_OR = roads.push(
new Roads(
locations[0].x,
locations[0].y,
locations[1].x,
locations[1].y,
"HWY1"
)
);
let OR_WA = roads.push(
new Roads(
locations[1].x,
locations[1].y,
locations[2].x,
locations[2].y,
"HWY1"
)
);
let CA_NV = roads.push(
new Roads(
locations[0].x,
locations[0].y,
locations[3].x,
locations[3].y,
"I80"
)
);
let NV_UT = roads.push(
new Roads(
locations[3].x,
locations[3].y,
locations[4].x,
locations[4].y,
"I80"
)
);
let UT_CO = roads.push(
new Roads(
locations[4].x,
locations[4].y,
locations[5].x,
locations[5].y,
"I15"
)
);
function drawRoads() {
for (let i = 0; i < roads.length; i++) {
roads[i].draw();
getAngle(roads[i], roads[i].name);
}
}
drawRoads();
function drawNames(ang, x1, y1, name) {
ctx.font = "10px arial";
ctx.textAlign = "center";
ctx.save();
ctx.translate(x1, y1);
ctx.rotate(ang);
if (name == "I15") {
ctx.rotate(-ang);
}
if (name == "I80") {
ctx.fillText(name, 0, 15);
} else if (name == "I15") {
ctx.fillText(name, 15, 0);
} else {
ctx.fillText(name, 0, -5);
}
ctx.restore();
}
function getAngle(roads, name) {
let dx = roads.x1 - roads.x2;
let dy = roads.y1 - roads.y2;
let midX = roads.x1 + (roads.x2 - roads.x1) * 0.5;
let midY = roads.y1 + (roads.y2 - roads.y1) * 0.5;
drawNames(Math.atan2(-dy, -dx), midX, midY, name);
}
<canvas id="canvas"></canvas>
Upvotes: 1
Reputation: 41
I am not sure if you are dealing with maps, but you could look at leaflet. It can draw objects to maps, circles, lines, polygons, texts etc. https://leafletjs.com/examples.html
Kind regards, Kristóf
Upvotes: 0