Search code examples
javascriptd3.jsmathsvgcoordinate-systems

math to svg coordinate translation in javascript


I have to work on a visualization script written in javascript and svg using d3.

Now I am faced eith the different coordinates systems.

  • Mathematical coordinates (left to right, bottom to top) for source data
  • SVG coordinates (left to right, top to bottom) for svg canvas

Does d3 or svg offer a way to setup a canvas with a transformation to adopt the math model, or shall I just do a coordiante conversion in code?


Solution

  • There are several different solutions, from the hacky transform = "scale(1,-1)" to pure JavaScript functions for converting the coordinates.

    However, the simplest idiomatic D3 solution is using a scale. For instance, a scale like this...

    const scale = d3.scaleLinear()
        .domain([0, height])
        .range([height, 0])
    

    ... will quite easily invert the SVG top-bottom vertical axis, to the more common bottom-up math axis.

    Here is a demo. First the normal code, plotting some circles with 0, 0, 2, 2, 4, 4 etc. as the data :

    const data = d3.range(100).map((d, i) => ({
      x: i * 2,
      y: d * 2
    }));
    const svg = d3.select("svg");
    const circles = svg.selectAll(null)
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 1)
      .attr("cx", d => d.x)
      .attr("cy", d => d.y)
    svg {
      background-color: lavender;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg></svg>

    Now the same code, but using a scale for inverting the vertical coordinates:

    const data = d3.range(100).map((d, i) => ({
      x: i * 2,
      y: d * 2
    }));
    const scale = d3.scaleLinear()
      .domain([0, 150])
      .range([150, 0]);
    const svg = d3.select("svg");
    const circles = svg.selectAll(null)
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 1)
      .attr("cx", d => d.x)
      .attr("cy", d => scale(d.y))
    svg {
      background-color: lavender;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg></svg>