Is there a way to flip the SVG coordinate system so that [0,0] is in the lower left instead of the upper left?
9 Answers
I have done a lot of experimentation, and the only logical method is as follows:
<g transform="translate(0,400)">
<g transform="scale(1,-1)">
Where 400 is the height of the image. What this does it move everything down so that the top of the image is now and the bottom of the image, then the scale operation flips the Y coordinates, so that the bit that is now off the page/image is flipped back up to fill the space left behind.

- 20,110
- 21
- 77
- 129
-
5Do you need the first translate? I've already accomplished that through the `viewbox="-200 -200 400 400"`. – SimplyKnownAsG Apr 04 '14 at 21:17
-
9These operations can be combined on one line.
– Paul Williams May 05 '16 at 17:07 -
27But now the text is upside down. – Chris Sattinger May 18 '16 at 12:33
-
4You can use a single `matrix` transformation instead of separate `translate` and `scale` transformations. To flip the y-axis, try `
` (400 being the height of the image). – r3mainer Mar 09 '17 at 04:18 -
@squeamishossifrage what about flipping the x-axis? – Sasha Fonseca Sep 29 '18 at 20:40
-
@SashaFonseca `
` (assuming a width of 400) – r3mainer Sep 29 '18 at 22:56 -
3@felix how did you fix the upside text problem? – sourabh dadapure May 14 '19 at 15:52
-
hmmm if you are coding then you can easily flip it in your code by doing your SVG layer height - y path coordiante – user889030 Jul 09 '21 at 06:05
The best all around combo I've found for transforming to a cartesian coordinate system is pretty simple:
css
svg.cartesian {
display:flex;
}
/* Flip the vertical axis in <g> to emulate cartesian. */
svg.cartesian > g {
transform: scaleY(-1);
}
/* Re-flip all <text> element descendants to their original side up. */
svg.cartesian > g text {
transform: scaleY(-1);
}
<html>
<head></head>
<body>
<svg class="cartesian" viewBox="-100 -100 200 200" preserveAspectRatio="xMidYMid meet">
<g>
<!-- SVG Start -->
<!-- Vertical / Horizontal Axis: Can be removed if you don't want x/y axes. -->
<path d="M0 -100 V 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<path d="M-100 0 H 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<!-- Plotting: This is an example plotting two points at (20, 20) and (-50, -35), replace it with your data. -->
<g transform="translate(20, 20)">
<circle r="1" />
<text>(20, 20)</text>
</g>
<g transform="translate(-50, -35)">
<circle r="0.5" />
<text>(-50, -35)</text>
</g>
<!-- SVG End -->
</g>
</svg>
</body>
</html>
This will auto-unflip all the text elements on the page via the css scaleY(-1)
.

- 43,029
- 15
- 76
- 119

- 17,444
- 7
- 59
- 72
-
6
-
1Thanks! I'm glad I wrote this down. I refer back to it whenever I need to do this. I just updated with comments explaining what the parts do so it's easier to use. – cchamberlain Apr 25 '19 at 12:32
-
1This could use more explanation. Got it working but not trivially. I would add some, but don't understand it all yet... – Tony May 12 '19 at 03:15
-
1@Tony - I added more information to the comments. Let me know whats confusing specifically and I'll try to improve it. – cchamberlain Jun 03 '19 at 22:11
-
1Thanks for the update. I added a couple comments to the CSS, I think it's pretty understandable now. – Tony Jun 05 '19 at 20:44
-
It looks like your comments got rejected by others. I paraphrased but kept condense since a lot of text becomes overwhelming. – cchamberlain Jun 11 '19 at 22:27
-
-
-
Coming at this very late, but could you put the class on the group element, just in case you wanted other nested groups? – Peter Du Jun 25 '21 at 01:57
I know this is old, but I was doing the same thing, tried @Nippysaurus version but this is too annoying since everything will be rotated (so if you put images, you'll have to rotate them back). There's another solution though
What I did was simply move the viewBox of the svg
and invert all coordinates on the y axis (and removing the height of the object to be at the bottom left corner on it too), like:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="300" viewBox="0 -300 200 300">
<rect x="20" y="-40" width="20" height="20" stroke="black" stroke-width="1px"></rect>
</svg>
this will put a rect
at 20,20 from the bottom left corner of the svg
, see http://jsfiddle.net/DUVGz/

- 2,912
- 3
- 35
- 59
-
At first sight this seems a kludge, however, having tried all of the other solutions, I have to agree with @Guillaume that all of the `transform` solutions are a PIA due to the effect on text, etc..., so this is the solution I've adopted for SVG output. – Grimxn Dec 19 '15 at 12:40
-
But how will transformations inside transformations fan out? Ie. nested groups ... – Feb 20 '17 at 09:57
If you don't know the size of the svg than you can use CSS transformations for the whole SVG element:
#parrot {
transform-origin: 50% 50%; /* center of rotation is set to the center of the element */
transform: scale(1,-1);
}
Credits: https://sarasoueidan.com/blog/svg-transformations/#transforming-svgs-with-css

- 14,388
- 2
- 28
- 30
Yes, a coordinate rotation of -90 followed by a translation of + the height of your new figure should do it. There is an example at W3C.

- 42,753
- 9
- 87
- 112
-
I think what you had in mind is a rotate 180, followed by a translation of -w and -h. This does not work, [0,0] is still at the top left. – Nippysaurus Oct 03 '10 at 08:52
-
I suspect that your SVG implementation is non-conformant which isn't surprising for a (presumably) rarely used feature. An affine rotation of -90 moves the Cartesian quadrant IV (+x, -y) into quadrant I (+x, +y). A rotation of 180 degrees would put your reference frame into quadrant II with the origin in the lower left. The reason your self-answer works is that it is congruent to my original answer but likely uses better tested bits of your SVG interpreter of choice. Personally, I'd just calculate the affine transformation in my user-space code and be pessimistic about SVG capabilities. – msw Oct 03 '10 at 09:46
-
You are probably right. I am using Safari on OS X 10.6, so will test it on a Windows machine tomorrow. – Nippysaurus Oct 03 '10 at 11:44
<g transform="translate(0,400) scale(1,-1)">
which also equivalent to below
<g transform="scale(1,-1) translate(0,-400) ">

- 43
- 5
An alternative is to use D3 v4 scaleLinear to create a function that will do the swapping for you.
import * as d3 from "d3";
...
// Set the height to the actual value to where you want to shift the coords.
// Most likely based on the size of the element it is contained within
let height = 1;
let y = d3.scaleLinear()
.domain([0,1])
.range([height,0]);
console.log("0 = " + y(0)); // = 1
console.log("0.5 = " + y(0.5)); // = 0.5
console.log("1 = " + y(1)); // = 0
console.log("100 = " + y(100)); // = -99
console.log("-100 = " + y(-100)); // = 101

- 7,516
- 5
- 36
- 42
What I did normally is drawing/putting the coordinate at the negative value of y-axis on the cartesian plane. Later on transfer the coordinate by replacing the negative value of y-axis into positive value.

- 1
- 1
I think the simpliest way to rotate element for 180 deg is that you rotate for 180.1 deg;
transform="translate(180.1,0,0)"

- 1
-
translate takes only two arguments, i.e., x and y coordinates. I don't think it takes in 3D coordinates (x,y,z). If you are intended to use 3D coordinates, then you have to use translate3d() which takes 3 coordinates. Take a look at the documentation : https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translate – ShellZero Jan 20 '17 at 22:35