Plot GeoJSON line string on canvas using D3 projection - javascript

I am trying to plot a line string on a globe created using D3, but somehow it shows up wrong.
Demo
Here is how the projection and path are set up
var projection = d3.geo.orthographic()
.translate([width / 2, height / 2])
.scale(scale)
.clipAngle(90);
var path = d3.geo.path()
.projection(projection)
.context(context);
Then I just draw it onto the canvas (using the path function)
// the route
context.fillStyle = '#000';
context.strokeStyle = '#000';
context.beginPath();
path(route);
context.fill();
The route variable is a GeoJSONn line string, I can plot the route on the leaflet map and everything works as expected, however when I try to draw it on the globe, it just shows up wrong.
I think it has something to do with projections, but I don't what causes this. Does anyone have any idea how can I correct it?

Instead of filling your path drawn on the canvas, you need to just stroke it:
// the route
context.fillStyle = '#000';
context.strokeStyle = '#000';
context.beginPath();
path(route);
context.stroke(); // Just stroke the path
//context.fill(); // You don't want to fill the path
This will only draw the line as expected. Have a look at this working Plunk.

Alternatively, if you need to specify fill, just set it to the background color (here that is #ccc)
// the route
context.fillStyle = '#ccc';
context.strokeStyle = '#000';
context.beginPath();
path(route);
context.fill();
context.stroke();

Related

How to get true 1-pixel width black color line in p5.js [duplicate]

When i try to draw single pixel black line with the following code:
context.strokeStyle = '#000';
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.lineWidth = 1;
context.stroke();
context.closePath();
I have more then one pixel line with gray border. How to fix it?
Here is an example http://jsfiddle.net/z4VJq/
Call your function with these coordinates instead: drawLine(30,30.5,300,30.5);. Try it in jsFiddle.
The problem is that your color will be at an edge, so the color will be halfway in the pixel above the edge and halfway below the edge. If you set the position of the line in the middle of an integer, it will be drawn within a pixel line.
This picture (from the linked article below) illustrates it:
You can read more about this on Canvas tutorial: A lineWidth example.
You have to use context.translate(.5,.5); to offset everything by half a pixel. Its easy way for fix your problem
var canvas = document.getElementById("canvas1");
var context1 = canvas.getContext('2d');
context1.strokeStyle = '#000';
context1.beginPath();
context1.moveTo(10, 5);
context1.lineTo(300, 5);
context1.stroke();
var canvas2 = document.getElementById("canvas2");
var context2 = canvas2.getContext('2d');
context2.translate(.5,.5);
context2.strokeStyle = '#000';
context2.beginPath();
context2.moveTo(10, 5);
context2.lineTo(300, 5);
context2.stroke();
<div><canvas height='10' width='300' id='canvas1'>Обновите браузер</canvas></div>
<div><canvas height='10' width='300' id='canvas2'>Обновите браузер</canvas></div>

Which of the two overlapping graphs will ctx.fill method fill?

I have two slightly different pieces of code which produce different results. In the first piece, I draw the inside triangle first, then the outside square. In the second piece, I draw the outside square first, then the inside triangle. However, for the second piece of code, the entire square is filled. Why is this happening?
<body>
<canvas id="c" width="300" height="300"></canvas>
<script>
var c = document.getElementById('c');
var ctx = c.getContext('2d');
ctx.moveTo(75 ,75);
ctx.lineTo(125, 75);
ctx.lineTo(125, 125);
ctx.lineTo(75, 75);
ctx.fill();
ctx.moveTo(50, 50);
ctx.lineTo(150,50);
ctx.lineTo(50,150);
ctx.lineTo(50,50);
ctx.stroke();
</script>
</body>
The result of the above code
<body>
<canvas id="c" width="300" height="300"></canvas>
<script>
var c = document.getElementById('c');
var ctx = c.getContext('2d');
ctx.moveTo(50, 50);
ctx.lineTo(150,50);
ctx.lineTo(50,150);
ctx.lineTo(50,50);
ctx.stroke();
ctx.moveTo(75 ,75);
ctx.lineTo(125, 75);
ctx.lineTo(125, 125);
ctx.lineTo(75, 75);
ctx.fill();
</script>
</body>
The result of the above code
Use ctx.beginPath to clear old path and start a new one.
When you use path construction methods like, ctx.rect, ctx.arc, ctx.moveTo, ctx.lineTo, ctx.quadraticCurveTo, ctx.bezierCurveTo, ctx.ellipse, ctx.arcTo and ctx.closePath you are adding to the current path held in the 2D context memory.
When you call ctx.fill or ctx.stroke you render the current stored path. The path stored is continually added to with the 1st paragraphs path constructor methods. The path stays stored until you call ctx.beginPath which will clear the current path and start a new one.
Note that resizing the canvas will also clear the current path and reset the canvas context to its default state, though it is not recommended as a way to revert to the default state.
So to correctly render the two paths use ctx.beginPath to create a new path for each you want to render.
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
ctx.beginPath(); // begin a new path
// Note that the path is offset by 0.5
// A line is defined by its center. A line 1 pixel wide has half the line
// above and half below. If you render at the pixel boundary from 50,50 to 100,50
// half the line is rendered on the 49th row and half on the 50th row.
// Offsetting by 0.5 ensures the line is rendered on the pixels you want
// producing a better quality line.
ctx.moveTo(50.5, 10.5);
ctx.lineTo(150.5,10.5);
ctx.lineTo(50.5,110.5);
//ctx.lineTo(50,10);
ctx.closePath(); // use this as lineTo does not connect the start and end lines.
ctx.stroke();
ctx.beginPath();
ctx.moveTo(75 ,75);
ctx.lineTo(125, 75);
ctx.lineTo(125, 125);
ctx.lineTo(75, 75);
// you don't need to use closePath here as fill does not treat continuous path
// segments as special.
ctx.fill();
Note that when you use ctx.beginPath the method ctx.lineTo acts like ctx.moveTo if it is the first method called after the ctx.beginPath call. This can help reduce the complexity of rendering functions.
Correct use of ctx.closePath
It is a common mistake to think that ctx.closePath is related to ctx.beginPath. The function ctx.closePath is a path constructor function that adds to the current path by creating a line from the last added point to the last ctx.moveTo or first point after a ctx.beginPath call.
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
canvas.width = 400;
ctx.beginPath(); // begin a new path
ctx.beginPath();
ctx.moveTo(100.5, 10.5);
ctx.lineTo(150.5, 110.5);
ctx.lineTo(50.5, 110.5);
ctx.closePath(); // creates a line from (50,110) to (100,10)
ctx.moveTo(300.5, 10.5);
ctx.lineTo(350.5, 110.5);
ctx.lineTo(250.5, 110.5);
ctx.closePath(); // creates a line from (250,110) to (300,10)
ctx.stroke();
Why use ctx.closePath()
The ctx.closePath ensures that the ends of the shape are joined as a single path and will be rendered using ctx.lineJoin setting. If you don't use it then the path is open and the ctx.lineCap setting will be applied to the ends
The snippet shows the difference between ctx.closePath and using ctx.lineTo to create a line back to the start. Notice the join at the top of the triangle. The left one used ctx.closePath and the right used ctx.lineTo
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
canvas.width = 400;
ctx.lineWidth = 15;
ctx.lineJoin = "round";
ctx.lineCap = "butt";
ctx.beginPath();
ctx.moveTo(100,20);
ctx.lineTo(150,120);
ctx.lineTo(50,120);
ctx.closePath(); // creates a line from (50,210) to (100,20)
// closed path with the line joined at the start
ctx.stroke()
ctx.beginPath();
ctx.moveTo(250,20);
ctx.lineTo(300,120);
ctx.lineTo(200,120);
ctx.lineTo(250,20);
// path is open the last line back to the start is still open and
// will use ctx.lineCap to render the start and end
ctx.stroke()

Canvas special shape - animating

I'm finishing a project, but I have one more step to finish.
I want to visualize microphone input by a canvas.
Getting the data from the microphone isn't a problem.
But I want to visualize it in a special way. (see image)
I want to animate each element from the wave.
My problem isn't the animation.
My problem is to create those shapes in the CANVAS.
This is an example of one shape:
I can create a rounded corner shape with the canvas
const draw = () => {
fillRoundedRect(20, 20, 100, 100, 20);
ctx.fillStyle = "red";
ctx.fill();
};
const fillRoundedRect = (x, y, w, h, r) => {
ctx.beginPath();
ctx.moveTo(x+r, y);
ctx.lineTo(x+w-r, y);
ctx.quadraticCurveTo(x+w, y, x+w, y+r);
ctx.lineTo(x+w, y+h-r);
ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
ctx.lineTo(x+r, y+h);
ctx.quadraticCurveTo(x, y+h, x, y+h-r);
ctx.lineTo(x, y+r);
ctx.quadraticCurveTo(x, y, x+r, y);
ctx.fill();
};
Can someone help me with creating a shape like in the second image?
Thanks in advance guys!
Instead of trying to make a single shape with dependency on surrounding shapes and a high risk of headache math-wise, use instead two shapes which you merge using composition. My suggestion anyways.
Draw all the bars in full height using composition mode source-over (default)
Define a single shape on top using some sort of spline (I would suggest a cardinal spline).
Set composition mode to destination-out and render an enclosed shape using the spline as top "line".
Example
This should work in a loop (remember to clear canvas for each frame) but shows only the building stones needed here -
var ctx = c.getContext("2d");
var points = [];
var skippy = 0;
// render all bars
ctx.globalCompositeOperation = "source-over"; // not needed here, but in a loop yes!
// produce bars
ctx.beginPath(); // not needed here, but in a loop yes!
for(var x = 0; x < c.width; x += 30) {
ctx.rect(x, 0, 16, c.height)
// OKIDOKI, lets produce the spline using random points (y) as well
// but not for all, only every second for prettyness... modify to taste
if (skippy++ % 2 === 0) points.push(x, c.height * Math.random());
}
points.push(c.width, c.height * Math.random()); // one last
ctx.fillStyle = "rgb(198, 198, 198)";
ctx.fill();
// render spline
ctx.beginPath();
ctx.moveTo(0, c.height); // bottom left corner
curve(ctx, points); // spline
ctx.lineTo(c.width, c.height); // bottom right corner
ctx.closePath();
ctx.globalCompositeOperation = "destination-out";
ctx.fill();

Inner line not removed from circle in Canvas

Right now I am playing around with Canvas and its features, however I am experiencing a weird problem. I am currently trying to draw a circle inside of a Triangle made of several "lineTo's". My problem being when I just implement an arc; it draws the Circle but with a line from the center and out towards the right side (angle 0). If I enclose my arc in beginPath and closePath it draws it perfectly however the Triangle disappears (JSFiddle is provided). Why is this happening also am I doing something specifically wrong? I am new to Canvas and I want to learn, thank you in advance!
JSFIDDLE https://jsfiddle.net/720hg2aq/1/
// Draw Triangle
ctx.moveTo((width*0.4), (height*0.05));
ctx.lineTo((width*0.6), (height*0.05));
ctx.moveTo((width*0.6), (height*0.05));
ctx.lineTo((width*0.5), (height*0.15));
ctx.moveTo((width*0.5), (height*0.15));
ctx.lineTo((width*0.4), (height*0.05));
ctx.moveTo((width*0.5), (height*0.09));
// End of Triangle
// Begin Circle
ctx.beginPath();
ctx.arc((width*0.5), (height*0.09), 20, 0 , 2 * Math.PI);
ctx.closePath();
// End Circle
// Draw it out
ctx.strokeStyle = '#000';
ctx.stroke();
The beginPath() method begins a path, or resets the current path.
Once you begin a path, use moveTo(), lineTo(), quadricCurveTo(), bezierCurveTo(), arcTo(), or arc() to make the path, then storke() it.
So the code should be:
ctx.strokeStyle = "black";
// Draw Triangle
ctx.beginPath(); /// Let's start the work!
ctx.moveTo((width*0.4), (height*0.05));
ctx.lineTo((width*0.6), (height*0.05));
ctx.moveTo((width*0.6), (height*0.05));
ctx.lineTo((width*0.5), (height*0.15));
ctx.moveTo((width*0.5), (height*0.15));
ctx.lineTo((width*0.4), (height*0.05));
ctx.moveTo((width*0.5), (height*0.09));
ctx.stroke(); /// Brushing with your dye!!
// End of Triangle
// Begin Circle
ctx.beginPath(); /// Let's start the **NEW** work!
/// Don't let the previous path be connected with the current path!
ctx.arc((width*0.5), (height*0.09), 20, 0 , 2 * Math.PI);
ctx.closePath();
ctx.stroke(); /// Brushing with your dye!!
// End Circle

Trying to draw arc in canvas - weird behavior

I am trying to make a circle out of arcs (something similar to a donut chart is what i am trying to achieve visually) and I succeeded. But, the edges look like a 4 year old drew them!
This is how i'm drawing my arcs:
var arc = new Kinetic.Shape({
drawFunc: function(canvas) {
var context = canvas.getContext('2d');
var x = Math.round(canvas.width / 2);
var y = Math.round(canvas.height / 2);
var radius = 210;
var startAngle = 1.44 * Math.PI;
var endAngle = 1.83 * Math.PI;
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = 175;
canvas.fillStroke(this);
},
stroke: '#121b21',
strokeWidth: 175
});
I created an example fiddle.
I am new to canvas so i figured its probably me...
Can someone please let me know if i am doing something wrong here?
Thank you!
This is the thick-stroke-arc bug present in WebKit browsers. It's probably due to some rounding problem in the arc drawing code. You'll find it looks fine in other browsers most likely. For Webkit, you can use another method to draw these wedges meanwhile.
Possible workarounds:
Use the method you have now, but put a white circle in the middle once you're done with the wedges to blot out the interior irregularities
Use Kinetic.Wedge instead of context.arc.

Categories

Resources