How to draw a svg in multiple locations, on top of another svg image

8.1k views Asked by At

I have drawn a floor map of a super market using MS Visio 2013. I have converted it to a svg file. What i want to do is to pin point some locations of the floor map using another svg file.

I have tried this by many other means. I created a html 5 canvas and drew the map using javascript commands.Then i used a svg image to show the locations.

ctx.drawSvg('location.svg', x_coordinate , y_coordinate , 10, 14);

//x_coordinate,y_coordinate is defining the multiple locations which this location.svg file will be drawn.

But results of that method was low in quality. Not to mention the fact that it gets more low quality when you zoom in to the map.

I know the method of embedding a svg to a html page or using a svg file as a background.But with those two how can i use another svg file to pinpoint the multiple locations?

Is there any way of doing this using svg files? :)

1

There are 1 answers

12
Paul LeBeau On BEST ANSWER

This is actually quite simple. There are several ways to do it, but the following way is probably one of the simplest. It doesn't use Canvas at all, just pure SVG.

I am going to assume that when you say the "pin" is another file, that is not really a strict requirement. Ie. that there is no reason you couldn't include the pin picture in your map SVG file.

Here's a sample SVG map file. I will assume for now that it is embedded in the HTML file, but an external file should work as well.

<html>
<body>
<svg id="map" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="500" height="400" viewBox="0 0 500 400">

  <defs>
      <!-- Define our map "pin". Just a circle in this case.
           The circle is centred on (0,0) to make positioning at the destination point simpler. -->
      <g id="pin">
          <circle cx="0" cy="0" r="7" fill="white" stroke="green" stroke-width="2"/>
      </g>
  </defs>

  <!-- A simple floorplan map -->
  <g id="floor" fill="#ddd" stroke="black" stroke-width="3">
      <rect x="50" y="50" width="200" height="150" />
      <rect x="100" y="200" width="150" height="150" />
      <rect x="250" y="100" width="200" height="225" />
  </g>

  <!-- A group to hold the created pin refernces. Not necessary, but keeps things tidy. -->
  <g id="markers">
  </g>
</svg>
</body>
</html>

The group "floor" is our floor plan, and the pin image has been included in the <defs> section. Stuff defined in the <defs> section is not rendered itself. It has to be referenced elsewhere in the file.

From here all you need is a simple Javascript loop which uses the DOM to add one <use> element for each pin.

var  markerPositions = [[225,175], [75,75], [150,225], [400,125], [300,300]];

var svgNS = "http://www.w3.org/2000/svg";
var xlinkNS = "http://www.w3.org/1999/xlink";

for (var i=0; i<markerPositions.length; i++) {
    // Create an SVG <use> element
    var  use = document.createElementNS(svgNS, "use");
    // Point it at our pin marker (the circle)
    use.setAttributeNS(xlinkNS, "href", "#pin");
    // Set it's x and y
    use.setAttribute("x", markerPositions[i][0]);
    use.setAttribute("y", markerPositions[i][1]);
    // Add it to the "markers" group
    document.getElementById("markers").appendChild(use);
}

The <use> allows us to make a reference to another element in the SVG file. So we create a <use> element for each pin we want to place. Each <use> references our predefined pin symbol.

Here is a demo: http://jsfiddle.net/6cFfU/3/

Update:

To use an external "pin" file, referencing it from an image should work.

<g id="pin">
  <image xlink:href="pin.svg"/>
</g>

Demo here: http://jsfiddle.net/6cFfU/4/

If you are not even allowed to reference the pin file from your map file. Then you just need to insert this marker definition using a bit of DOM manipulation. Something like:

var  grp = document.createElementNS(svgNS, "g");
grp.id = "pin";
var  img = document.createElementNS(svgNS, "image");
img.setAttributeNS(xlinkNS, "href", "pin.pvg");
grp.appendChild(img);
document.getElementsByTagName("defs")[0].appendChild(grp);

Demo here: http://jsfiddle.net/7Mysc/1/

That's assuming you want to go the pure SVG route. There are other ways. For instance you could do a similar thing using HTML. Wrap your PIN file in a <div> and use jQuery to clone the div, then use absolute positioning to position them them in the correct place. However you then have to worry about adjusting for the map scale etc.