Alan
Alan

Reputation: 2086

Convert SVG Paths to Rect

I want to auto generate an imagemap type of result for a raster image. I was able to supply this image as a PNG:enter image description here

The original SVG for this looks like this:

<svg width="580" height="400" xmlns="http://www.w3.org/2000/svg">
 <g>
  <rect fill="#fff" id="canvas_background" height="402" width="582" y="-1" x="-1"/>
  <g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
   <rect fill="url(#gridpattern)" stroke-width="0" y="0" x="0" height="100%" width="100%"/>
  </g>
 </g>
 <g>
  <rect id="svg_1" height="67" width="54" y="119.5" x="125.5" stroke-width="1.5" stroke="#000" fill="#fff"/>
  <rect id="svg_3" height="67" width="54" y="119.5" x="180.5" stroke-width="1.5" stroke="#000" fill="#fff"/>
 </g>
</svg>

Once I traced it using the library: https://github.com/jankovicsandras/imagetracerjs I get back path data like this:

<svg width="156" height="114" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.3" >
    <path fill="rgb(60,60,60)" stroke="rgb(60,60,60)" stroke-width="1" opacity="1" d="M 20 20 L 131 20 L 131 89 L 20 89 L 20 20 Z M 22 22 L 22 87 L 74 87 L 74 22 L 22 22 Z M 77 22 L 77 87 L 129 87 L 129 22 L 77 22 Z " />
    <path fill="rgb(255,255,255)" stroke="rgb(255,255,255)" stroke-width="1" opacity="1" d="M 0 0 L 156 0 L 156 114 L 0 114 L 0 0 Z M 20 20 L 20 89 L 131 89 L 131 20 L 20 20 Z " />
    <path fill="rgb(255,255,255)" stroke="rgb(255,255,255)" stroke-width="1" opacity="1" d="M 22 22 L 74 22 L 74 87 L 22 87 L 22 22 Z " />
    <path fill="rgb(255,255,255)" stroke="rgb(255,255,255)" stroke-width="1" opacity="1" d="M 77 22 L 129 22 L 129 87 L 77 87 L 77 22 Z " />
</svg>

I would like to go back to the rect or polygon method so I can measure the area of each object so that if there were traced text I could exclude it / flatten it by saying it's total area is lower than allowed as a polygon / rect object.

Is there a way to convert the path data back to where I have 2 separate objects? I want to be able to overlay the results over the original image and allow targeting each square

Upvotes: 4

Views: 4692

Answers (1)

Andras
Andras

Reputation: 196

I try to answer your question as best as I can, but there are multiple solutions here.

If you force imagetracerjs to use only straight line edges (with qtres = 0.00001) , then SVG path elements are polygons, their coordinates are defined in the d attribute: d="M 20 20 L 131 20 ... " in the first example. (More info: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d) But 1 d attribute is often not just 1 polygon. The shape can include holes, and they are represented as smaller polygons. (More info: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule)

You can extract the d attributes with e.g. Regex, split them by the Z tags (which mean roughly: this shape ends, a hole shape begins) , then use the Gauss's area formula (https://en.wikipedia.org/wiki/Shoelace_formula) on the coordinates to get the areas. You might need to subtract the areas of the holes from the area of the parent shape.

You have 4 separate objects on your image, but some are not trivial. I try to "translate" your SVG result so you can decide which path to process. ImageTracer output is sorted by colors first. The 1. path is the blackish frame around the two smaller rectangles, technically this is a blackish rectangle with two rectangle holes. The 2. path is white frame around the blackish frame, technically a white rectangle with a big rectangle hole. The 3. and 4. paths are the smaller white rectangles, which might be relevant for you.

There's an alternative to splitting and parsing the SVG string, but it's a bit undocumented, sadly. You can get a tracedata object first, process it, and optionally render it to SVG string later. (More info: https://github.com/jankovicsandras/imagetracerjs#examples )


var options = {qtres:0.0001};

ImageTracer.imageToTracedata(
       'example.png',
       function(tracedata){

           // color layers loop
           for(var i=0; i<tracedata.layers.length; i++){
               // paths loop
               for(var j=0; j<tracedata.layers[i].length; j++){

                   var thispath = tracedata.layers[i][j];

                   // processing this path if it's not a hole
                   if( !thispath.isholepath ){

                       // accumulating coordinates in [ [x,y] , [x,y] , ... ] polygon
                       var thispolygon = [];
                       thispolygon.push( [ thispath.segments[0].x1 , thispath.segments[0].y1 ] );
                       for(var k=0; k<thispath.segments.length; k++){ thispolygon.push( [ thispath.segments[k].x2 , thispath.segments[k].y2 ] ); }

                       // TODO: calculate thispolygon area with https://en.wikipedia.org/wiki/Shoelace_formula here
                   }


               }// End of paths loop
           }// End of color layers loop


       },// End of imageToTracedata callback()

       options

);// End of ImageTracer.imageToTracedata()


I hope these help. :)

Upvotes: 2

Related Questions