Search code examples
javascriptsvgpolygonpoint

How do I set points on an svg polygon using variables defined by a source XML file?


I cannot for the life of me find out how, or if its even possible, to set points on an svg polygon using variables that are defined by an XML document that is always changing.

I just want to set the path to something along the lines of

var polygonIwant = window.document.createElementNS(svgns,"polygon");
polygonIwant.setAttributeNS(null,"points", "M"+var1+", "+var2+", L"+var3+" ...etc);
polygongroup.appendChild(polygonIwant);

is there just not a way to do this?

I tried what I put above, but kept getting errors when running it.

I'm using vanilla javascript, and its going into a report generated in jaspersoft studio.

I would even settle for being able to create a standard shape, and just have the shapes coordinates change. No idea if that is possible either.

XML Data:

    <mach_name>BASE-VISUALIZATION</mach_name>
    <mach_keywords>BASE-SHAPE</mach_keywords>
    <mach_keywords_list>
    <keyword>BASE-SHAPE</keyword>
    </mach_keywords_list>
    <Depth>12</Depth>
    <Cstmr>3.5</Cstmr>
    <Vndr>4</Vndr>
    <Bheight>2</Bheight>
    <TYPE>4</TYPE>

variables:

    var svg = 
    window.document.createElementNS("http://www.w3.org/2000/svg","svg");
    svg.setAttributeNS(null,"height",instanceData.height);
    svg.setAttributeNS(null,"width",instanceData.width);   
    
    var b=((instanceData.width<instanceData.height)? 
    instanceData.width:instanceData.height)/10;

    var bx=b;
    var by=b;
    var dx=instanceData.width-b*2;
    var dy=instanceData.height-b*2;
    var scalew=dx/width*.75;
    var scaleh=dy/height*.75;
    var scale=(scalew<scaleh)?(scalew):scaleh;  

    bx+=(dx-width*scale)/2;
    by-=(dy-height*scale)/2;

    var depth=parseFloat(instanceData.Depth);
    var bwidth=parseFloat(instanceData.basewidth);
    var Cstmr=parseFloat(instanceData.Cstmr);
    var Vndr=parseFloat(instanceData.Vndr);
    var jw=4.5*scale
    var x=0
    var y=0

    brJambGroup=document.createElementNS(svgns,"g");
    brJambGroup.setAttribute("name","mygroup");

    var brjambshape = window.document.createElementNS(svgns,"polygon");
    brjambshape.setAttributeNS(null, "points",(bx+(depth-Cstmr-4.5)*scale) 
    (by+dy+height*scale), (bx+(depth-Cstmr)*scale) (by+dy+height*scale), 
    (bx+(depth-Cstmr)*scale) (by+dy+(height-2.5)*scale), (bx+(depth- 
    Cstmr-.75)*scale) (by+dy+(height-2.5)*scale), (bx+(depth- 
    Cstmr-.75)*scale) (by+dy+(height-1.75)*scale), (bx+(depth-Cstmr- 
    4.5)*scale) (by+dy+(height-1.75)*scale), (bx+(depth-Cstmr-4.5)*scale) 
    (by+dy+height*scale));
    brjambshape.setAttributeNS(null, "stroke","red");
    brjambshape.setAttributeNS(null,"fill","none");
    brJambGroup.appendChild(brjambshape);

    if(TYPE.toString()=="4")
    {
        svg.appendChild(basegroup);
        //svg.appendChild(nbrjambgroup);
        svg.appendChild(brJambGroup);
    }

The variables that can be input are: Height: 34 Width: 12 Type: 1 depth: 12 bwidth: 34 Cstmr: 3.5 Vndr: 4 jw: 4.5scale jh: 1.75scale x=0 y=0


Solution

  • As commented the your current script doesn't create a valid point array to be applied to a <polygon> point attribute.

    Provided, the XML parsing works correctly as well as your coordinate scaling the part for the point coordinates should look something like this:

    // create polygon point array
    let points = [
      (bx + (depth - Cstmr - 4.5) * scale), (by + dy + Height * scale),
      (bx + (depth - Cstmr) * scale), (by + dy + Height * scale),
      (bx + (depth - Cstmr) * scale), (by + dy + (Height - 2.5) * scale),
      (bx + (depth - Cstmr - 0.75) * scale), (by + dy + (Height - 2.5) * scale),
      (bx + (depth - Cstmr - 0.75) * scale), (by + dy + (Height - 1.75) * scale),
      (bx + (depth - Cstmr - 4.5) * scale), (by + dy + (Height - 1.75) * scale),
      (bx + (depth - Cstmr - 4.5) * scale), (by + dy + Height * scale)
    ];
    // set points to polygon attribute
    brjambshape.setAttribute("points", points.join(' '));  
    

    The main issue in your current code: you haven't separated x and y coordinates by a comma.

    As a side note: you don't need createElementNS() for svg elements unless they use a custom namespace (a lot of graphic applications use these to store app specific metadata - not needed for browser display).

    let xml = `<mach_name>BASE-VISUALIZATION</mach_name>
        <mach_keywords>BASE-SHAPE</mach_keywords>
        <mach_keywords_list>
        <keyword>BASE-SHAPE</keyword>
        </mach_keywords_list>
        <Depth>12</Depth>
        <Cstmr>3.5</Cstmr>
        <Vndr>4</Vndr>
        <JH>1.75</JH>
        <Width>12</Width>
        <Height>34</Height>
        <BHeight>2</BHeight>
        <BWidth>2</BWidth>
        <TYPE>4</TYPE>`;
    
    
    // parse xml to JS object
    let instanceData = xmlStringToJSO(xml);
    // properties to variables
    let {
      Width,
      Height,
      Depth,
      BWidth,
      Cstmr,
      Vndr,
      BHeight,
      TYPE
    } = instanceData;
    
    
    // create svg
    let svgns = "http://www.w3.org/2000/svg";
    var svg = window.document.createElementNS(svgns, "svg");
    svg.setAttribute("height", Height);
    svg.setAttribute("width", Width);
    
    var b = (Width < Height ? Width : Height) / 10;
    var bx = b;
    var by = b;
    var dx = Width - b * 2;
    var dy = Height - b * 2;
    var scalew = (dx / Width) * 0.75;
    var scaleh = (dy / Height) * 0.75;
    var scale = scalew < scaleh ? scalew : scaleh;
    
    bx += (dx - Width * scale) / 2;
    by -= (dy - Height * scale) / 2;
    
    var depth = Depth;
    var bWidth = BWidth;
    
    var jw = 4.5 * scale;
    var x = 0;
    var y = 0;
    
    let brJambGroup = document.createElementNS(svgns, "g");
    brJambGroup.id = "mygroup";
    
    let basegroup = document.createElementNS(svgns, "g");
    basegroup.id = "baseGroup";
    
    var brjambshape = document.createElementNS(svgns, "polygon");
    
    // create polygon point array
    let points = [
      (bx + (depth - Cstmr - 4.5) * scale), (by + dy + Height * scale),
      (bx + (depth - Cstmr) * scale), (by + dy + Height * scale),
      (bx + (depth - Cstmr) * scale), (by + dy + (Height - 2.5) * scale),
      (bx + (depth - Cstmr - 0.75) * scale), (by + dy + (Height - 2.5) * scale),
      (bx + (depth - Cstmr - 0.75) * scale), (by + dy + (Height - 1.75) * scale),
      (bx + (depth - Cstmr - 4.5) * scale), (by + dy + (Height - 1.75) * scale),
      (bx + (depth - Cstmr - 4.5) * scale), (by + dy + Height * scale)
    ];
    // set points to polygon attribute
    brjambshape.setAttribute("points", points.join(' '));
    brjambshape.setAttribute("stroke", "red");
    brjambshape.setAttribute("fill", "none");
    brJambGroup.appendChild(brjambshape);
    
    
    if (TYPE.toString() == "4") {
      svg.appendChild(basegroup);
      //svg.appendChild(nbrjambgroup);
      svg.appendChild(brJambGroup);
    }
    document.body.append(svg)
    
    // adjust viewBox
    let bb = svg.getBBox();
    svg.setAttribute('viewBox', [bb.x, bb.y, bb.width, bb.height].join())
    
    
    /**
     * xml to JSO helper
     */
    
    function xmlStringToJSO(xmlString) {
      const xml2Jso = (xml) => {
        try {
          var obj = {};
          if (xml.children.length > 0) {
            for (var i = 0; i < xml.children.length; i++) {
              var item = xml.children.item(i);
              var nodeName = item.nodeName;
              if (typeof(obj[nodeName]) == "undefined") {
                obj[nodeName] = xml2Jso(item);
              } else {
                if (typeof(obj[nodeName].push) == "undefined") {
                  // convert to numbers
                  var val = isFinite(obj[nodeName]) ? parseFloat(obj[nodeName]) : obj[nodeName];
                  obj[nodeName] = [];
                  obj[nodeName].push(val);
                }
                obj[nodeName].push(xml2Jso(item));
              }
            }
          } else {
            obj = isFinite(xml.textContent) ? parseFloat(xml.textContent) : xml.textContent;
          }
          return obj;
        } catch (e) {
          console.log(e.message);
        }
      }
      let xmlDoc = new DOMParser().parseFromString(`<xmlroot>${xmlString}</xmlroot>`, "text/xml").querySelector('xmlroot')
      let jso = xml2Jso(xmlDoc)
      return jso;
    }

    If your data source (application/API) also provides a JSON export, prefer this option as it simplifies data parsing.