Search code examples
csssvgcentering

Center vertically et horizontally some numbers on multiple complex paths on SVG


I would like to center vertically and horizontally some numbers on each paths on this SVG.

For now, I could achieve this when I set arbitrary X and Y values, for example. :

<path id="path-44" d="xxxxxxxxxxxx"/>
<text class="numero-dep" text-anchor="middle" xlink:href="#path-44" x="142" y="242">44</text>

It works but I have to manually do this for more than hundred paths (on the SVG example, I've deleted almost all paths in order to simplify the question. But it is a map of France and I have more than 100 paths

Is there a way to center automatically horizontally and vertically some text on some path?

For now, I have tried to center the text with css techniques (transform property, flexbox, grid) but it is not working.

I've also tried to use textPath but I can't center with this element. What I found interesting with textPath, it has an href attribute with a reference to the element. So with reference, I was hoping to center the text but it seems not possible.

.map__image path {
  fill: red;
  stroke: #fff;
  stoke-width: 1px;
  transition: color 0.3s;
}
<div class="map__image">

  <svg xmlns="http://www.w3.org/2000/svg" xmlns:amcharts="http://amcharts.com/ammap" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 612 585">
                <g>
                
                
                <a xlink:href="" xlink:title="Loire" id="FR-42">
                    <path class="land" d="M377,302.07L377.43,303.83L376.64,305.75L380.06,306.45L380.92,308.27L380.92,308.27L381.96,308.27L381.96,308.27L385.46,306.68L386.71,307.97L388.59,308.17L389.81,306.94L390.56,307.94L391.75,307.35L391.56,309.09L392.62,309.22L394.79,307.5L396.59,307.53L396.22,306.65L397.29,305.4L397.29,305.4L398.64,305.97L397.65,307.28L399.29,308.96L398.47,310.44L396.99,309.64L394.58,310.79L393.8,314.38L391.56,315.62L394.19,318.23L393.64,319.23L391.34,318.88L395.53,322.74L394.58,324.3L395.42,325.82L397.65,326.92L396.88,331.41L395.67,332.23L396.21,333.33L397.9,333.74L396.37,338.1L399.52,340.88L400.71,343.12L406.74,343.73L407.85,345.62L409.43,344.58L408.22,346.31L408.32,348.92L410.13,349.54L411.21,348.51L412.65,350.82L412.65,350.82L412.6,356.09L412.6,356.09L406.75,359.27L407.07,360.47L405.74,361.51L406.17,362.87L401.23,363.73L401.23,363.73L399.29,361.45L397.38,362.43L397.38,362.43L396.88,362.24L396.88,362.24L395.7,361.45L396.6,359.87L394.97,358.91L396.45,357.78L395.58,356.28L394.01,355.73L392.51,356.54L390.89,354.71L387.81,354.71L387.6,356.14L384.59,356.53L384.17,357.85L383.12,356.68L382.07,357.06L381.97,358.32L380.23,355.51L378.56,355.74L377.4,358.01L376.96,356.59L376.96,356.59L376.72,354.1L379.7,351.96L380.59,348.43L379.6,347.8L378.47,343.57L373.9,340.41L371.96,336.87L372.11,334.88L370.11,333.38L370.06,331.94L368.3,330.96L369.69,329.63L369.4,326.27L371.02,325.33L368.46,322.71L368.46,322.71L369.08,320.13L370.52,320.6L371.04,319.57L372.37,320.14L374.17,318.6L373.1,315.37L373.77,313.27L372.59,311.88L372.44,309.25L373.24,308.83L371.55,304.27L373.05,304.18L373,303.13L375.67,302.88L376.11,302.07L376.11,302.07z"/>
                </a>
                <a xlink:href="#test" xlink:title="Haute-Loire" id="FR-43">
                    <path class="land" d="M344.32,356.93L347.13,356.96L349.48,354.45L351.66,354.07L352.13,352.86L353.23,354.23L354.42,352.75L355.4,354L357.12,354.17L358.87,353.97L360.47,352.39L361.67,354.15L363.35,353.94L365.33,357.72L367.25,356.33L367.5,355.04L369.09,355.56L368.78,356.49L371.87,357.12L372.99,354.58L374.38,355.09L374.45,356.1L376.96,356.59L376.96,356.59L377.4,358.01L378.56,355.74L380.23,355.51L381.97,358.32L382.07,357.06L383.12,356.68L384.17,357.85L384.59,356.53L387.6,356.14L387.81,354.71L390.89,354.71L392.51,356.54L394.01,355.73L395.58,356.28L396.45,357.78L394.97,358.91L396.6,359.87L395.7,361.45L396.88,362.24L396.88,362.24L397.38,362.43L397.38,362.43L399.29,361.45L401.23,363.73L401.23,363.73L401.09,366.41L399.58,369.05L400.18,370.61L399.39,370.4L399.28,371.14L398.75,369.59L396.71,369.15L397.75,371.43L396.09,371.76L395.56,373.6L397.07,375.17L393.5,376.45L393.25,377.78L394.22,379.16L390.53,379.74L387.69,385.01L382.63,385.05L382.37,387.31L381.43,387.05L381.2,388.11L380.47,387.35L380.71,388.6L379.09,387.7L377.49,390.55L378.02,391.1L375.44,392.52L375.44,392.52L374.37,392.31L374.67,390.89L373.28,391.33L371.27,389.22L370.53,387.04L367.34,387.59L367.62,385.96L365.82,384.52L364.31,384.84L364.1,387.74L363.33,387.17L359.51,388.71L356.97,383.52L356.45,380.09L355.89,380.47L354.66,379.27L354.66,379.27L353.96,378.42L355.04,377.33L353.25,377.02L353.66,376.1L352.66,376.48L352.08,375.68L352.62,372.67L351.41,371.52L354.63,371.5L351.66,370.51L350.5,367.4L350.5,367.4L350.5,366.82L350.5,366.82L351,365.35L349.48,364.77L348.91,361.34L346.48,361.83L346.3,360.27L343.08,360.62L344.4,359.4L343.42,358.54L344.34,358.37z"/>


                </a>
                <a xlink:href="" xlink:title="Loire-Atlantique" id="FR-44">
                    <path id="path-44" class="land" d="M128.02,219.89l5.19,-3.85l0.76,1.38l3.94,-2.29l5.75,0.67l1.15,-0.93l1.49,0.17l1.49,-3.29l6.19,-2.43l-0.24,-1.99l3.76,0.44l1.03,1.79l4.84,1.39l0,0l0.51,1.26l-0.83,1.54l2.44,0.57l0.86,1.7l0,0l-0.07,0.61l0,0l-0.23,1.25l0.94,0.17l0.88,2.54l5.48,1.87l-1.52,1.62l-5.4,-0.59l0.66,3.84l7.86,1.08l0.78,4.4l1.08,1.27l-2.22,1.74l-6.98,0.11l-6.09,1.79l-2.61,2.17l2.42,-0.1l2.23,3.81l1.98,-0.52l1.29,4.09l0,0l-0.02,0.26l0,0l-3.08,2.67l0.59,1.1l-1.02,1.2l3.28,0.37l0.58,1.28l1.62,0.56l-0.02,1.22l-1.25,0.86l0,0l-2.25,-0.71l-2.86,-2.72l-1.24,3.08l-1.88,-0.67l-1.35,0.81l0.56,4.51l-4.07,1.85l-0.37,-6.31l-1.3,-0.47l-2.2,1.52l0.55,0.76l-0.59,1.34l1.56,2.36l-0.42,1.08l-0.69,-0.17l1.88,2.72l-1.8,0.39l-0.14,0.83l-4.75,-0.84l-1,-0.97l-1.48,0.38l-0.98,-2.69l-3.18,-0.12l-0.24,-1.39l-2.99,-0.48l-0.65,-1.77l-1.06,-0.18l-1.56,-2.18l0,0l-3.07,-3.98l-8.07,-2.34l3.36,-2.17l-0.21,-5.83l-1.67,-0.36l-3.54,2.18l-3.26,-2.68l-1.74,0.1l-0.6,1.12l-4.7,-1.94l1.5,-1.12l0.12,-1.81l-2.23,-2.2l4.18,-3.01l0.02,-1.49l0,0l1.43,-1.77l0.9,1.27l3.5,-0.29l0.78,-3.38l1.63,0.13l0.59,1.19l2.49,-1.11l-0.17,1.08l1.06,0.32l0.26,-1.95l2.46,-0.69l-0.11,-3.76l0.8,-1.06L128.02,219.89z"/>
                    <text class="numero-dep" text-anchor="middle" xlink:href="#path-44" x="142" y="242">44</text>
                </a>

                <a xlink:href="" xlink:title="Loiret" id="FR-45">
                    <path class="land" d="M297.89,179.38L299.84,179.51L302.49,178.11L304.93,178.63L304.44,177.54L305.67,177.83L306.57,175.79L308.34,176.55L308.32,178.63L309.22,177.59L310.59,177.81L310.59,177.81L311.05,176.75L311.05,176.75L311.81,176.5L313.05,178.05L314.93,177.56L314.93,177.56L315.74,181.04L319.15,182.38L319.86,184.74L319.18,187.44L318.21,186.93L316.57,189.46L319.8,189.44L321.37,188.4L327.44,189.49L329.46,188.13L328.72,186.8L329.82,187.63L331.32,186.68L331.41,188.98L332.17,189.09L334.11,187.46L337.05,187.03L337.05,187.03L340.89,188.88L340.33,189.95L341.15,189.89L341.73,192.6L344.84,195.29L343.79,195.93L344.86,196.72L344.98,198.82L343.63,200.35L343.63,200.35L343.14,200.69L343.14,200.69L341.75,202.64L340.05,202.94L340.2,204.95L339.49,205.34L341.02,206.42L340.21,207.42L340.72,210.3L333.51,212.12L333.35,214.61L334.62,214.4L334.64,215.43L336.6,216.77L336.3,217.59L337.73,219.03L336.86,220.17L337.03,221.87L338.69,223.68L338.69,223.68L337.95,224.44L336.13,223.9L334.39,225.18L333.72,224.72L333.33,225.53L334.45,226.65L334.45,226.65L331.65,228.23L328.52,225.57L328.22,228.27L325.88,228.89L325.4,227.32L323.67,226.46L323.75,225.42L321.52,223.52L318.54,223.75L316.32,221.21L313.59,222.69L310.17,220.02L308.06,220.56L308.06,220.56L308.15,219.29L306.58,217L302.81,217.56L298.51,216.99L296.67,218.31L295.71,218.2L295.53,216.9L292.59,217.21L292.29,216.15L291.81,218.23L289.6,219.56L287.38,218.09L286.27,213.61L282.82,212.07L280.65,214.08L281.58,213.09L279.38,211.39L280.31,209.69L278.48,208.25L281.19,204.65L280.81,203.19L278.35,201.38L279.69,200.08L280.1,197.8L278.28,198.27L278.28,198.27L277.9,195.49L279.41,195.32L279.3,194.44L281.33,195.27L281.53,194.45L282.4,194.54L282.57,192.82L284.01,192.41L284.46,193.26L285.22,192.35L287.55,193.22L289.74,192.39L289.93,191.57L291.04,192.25L293.28,191.09L294.79,188.09L294.14,187.24L294.96,186.38L296.98,186.41L296.03,185.07L296.69,184.63L296.95,185.29L297.28,184.54L296.56,181.91z"/>
                </a>
                
                
                </g>
                </svg>

</div>


Solution

  • As I've commented: You may center the text inside the bounding box at the path. You can get the position and the size of the bounding box by using the getBBox() method. However depending on the path the center of the bounding box may fall outside the path - as in the case of FR-42

    This is how I would do it:

    const SVG_NS = "http://www.w3.org/2000/svg";
    const theSvg = document.querySelector("svg")
    
    let a_paths = document.querySelectorAll("a path");
    
    a_paths.forEach(p =>{
      //get the bounding box of the path
      let bb = p.getBBox()
      //Get the text content
      let text_content = p.parentNode.id.split("-")[1];
      // create a new element text and set the x and y attributes and the text content
      let txt = document.createElementNS(SVG_NS, 'text');
      txt.setAttributeNS(null,"x",bb.x + bb.width/2);
      txt.setAttributeNS(null,"y",bb.y + bb.height/2);
      txt.textContent = text_content;
      // append the text to the svg element
      theSvg.appendChild(txt)
      })
    .map__image path {
      fill: red;
      stroke: #fff;
      stoke-width: 1px;
      transition: color 0.3s;
    }
    
    text{text-anchor:middle;}
    <div class="map__image">
    
      <svg xmlns="http://www.w3.org/2000/svg" xmlns:amcharts="http://amcharts.com/ammap"
        xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 612 585">
        <g>
    
          <a xlink:href="" xlink:title="Loire" id="FR-42">
            <path class="land"
              d="M377,302.07L377.43,303.83L376.64,305.75L380.06,306.45L380.92,308.27L380.92,308.27L381.96,308.27L381.96,308.27L385.46,306.68L386.71,307.97L388.59,308.17L389.81,306.94L390.56,307.94L391.75,307.35L391.56,309.09L392.62,309.22L394.79,307.5L396.59,307.53L396.22,306.65L397.29,305.4L397.29,305.4L398.64,305.97L397.65,307.28L399.29,308.96L398.47,310.44L396.99,309.64L394.58,310.79L393.8,314.38L391.56,315.62L394.19,318.23L393.64,319.23L391.34,318.88L395.53,322.74L394.58,324.3L395.42,325.82L397.65,326.92L396.88,331.41L395.67,332.23L396.21,333.33L397.9,333.74L396.37,338.1L399.52,340.88L400.71,343.12L406.74,343.73L407.85,345.62L409.43,344.58L408.22,346.31L408.32,348.92L410.13,349.54L411.21,348.51L412.65,350.82L412.65,350.82L412.6,356.09L412.6,356.09L406.75,359.27L407.07,360.47L405.74,361.51L406.17,362.87L401.23,363.73L401.23,363.73L399.29,361.45L397.38,362.43L397.38,362.43L396.88,362.24L396.88,362.24L395.7,361.45L396.6,359.87L394.97,358.91L396.45,357.78L395.58,356.28L394.01,355.73L392.51,356.54L390.89,354.71L387.81,354.71L387.6,356.14L384.59,356.53L384.17,357.85L383.12,356.68L382.07,357.06L381.97,358.32L380.23,355.51L378.56,355.74L377.4,358.01L376.96,356.59L376.96,356.59L376.72,354.1L379.7,351.96L380.59,348.43L379.6,347.8L378.47,343.57L373.9,340.41L371.96,336.87L372.11,334.88L370.11,333.38L370.06,331.94L368.3,330.96L369.69,329.63L369.4,326.27L371.02,325.33L368.46,322.71L368.46,322.71L369.08,320.13L370.52,320.6L371.04,319.57L372.37,320.14L374.17,318.6L373.1,315.37L373.77,313.27L372.59,311.88L372.44,309.25L373.24,308.83L371.55,304.27L373.05,304.18L373,303.13L375.67,302.88L376.11,302.07L376.11,302.07z" />
          </a>
          <a xlink:href="#test" xlink:title="Haute-Loire" id="FR-43">
            <path class="land"
              d="M344.32,356.93L347.13,356.96L349.48,354.45L351.66,354.07L352.13,352.86L353.23,354.23L354.42,352.75L355.4,354L357.12,354.17L358.87,353.97L360.47,352.39L361.67,354.15L363.35,353.94L365.33,357.72L367.25,356.33L367.5,355.04L369.09,355.56L368.78,356.49L371.87,357.12L372.99,354.58L374.38,355.09L374.45,356.1L376.96,356.59L376.96,356.59L377.4,358.01L378.56,355.74L380.23,355.51L381.97,358.32L382.07,357.06L383.12,356.68L384.17,357.85L384.59,356.53L387.6,356.14L387.81,354.71L390.89,354.71L392.51,356.54L394.01,355.73L395.58,356.28L396.45,357.78L394.97,358.91L396.6,359.87L395.7,361.45L396.88,362.24L396.88,362.24L397.38,362.43L397.38,362.43L399.29,361.45L401.23,363.73L401.23,363.73L401.09,366.41L399.58,369.05L400.18,370.61L399.39,370.4L399.28,371.14L398.75,369.59L396.71,369.15L397.75,371.43L396.09,371.76L395.56,373.6L397.07,375.17L393.5,376.45L393.25,377.78L394.22,379.16L390.53,379.74L387.69,385.01L382.63,385.05L382.37,387.31L381.43,387.05L381.2,388.11L380.47,387.35L380.71,388.6L379.09,387.7L377.49,390.55L378.02,391.1L375.44,392.52L375.44,392.52L374.37,392.31L374.67,390.89L373.28,391.33L371.27,389.22L370.53,387.04L367.34,387.59L367.62,385.96L365.82,384.52L364.31,384.84L364.1,387.74L363.33,387.17L359.51,388.71L356.97,383.52L356.45,380.09L355.89,380.47L354.66,379.27L354.66,379.27L353.96,378.42L355.04,377.33L353.25,377.02L353.66,376.1L352.66,376.48L352.08,375.68L352.62,372.67L351.41,371.52L354.63,371.5L351.66,370.51L350.5,367.4L350.5,367.4L350.5,366.82L350.5,366.82L351,365.35L349.48,364.77L348.91,361.34L346.48,361.83L346.3,360.27L343.08,360.62L344.4,359.4L343.42,358.54L344.34,358.37z" />
    
          </a>
          <a xlink:href="" xlink:title="Loire-Atlantique" id="FR-44">
            <path id="path-44" class="land"
              d="M128.02,219.89l5.19,-3.85l0.76,1.38l3.94,-2.29l5.75,0.67l1.15,-0.93l1.49,0.17l1.49,-3.29l6.19,-2.43l-0.24,-1.99l3.76,0.44l1.03,1.79l4.84,1.39l0,0l0.51,1.26l-0.83,1.54l2.44,0.57l0.86,1.7l0,0l-0.07,0.61l0,0l-0.23,1.25l0.94,0.17l0.88,2.54l5.48,1.87l-1.52,1.62l-5.4,-0.59l0.66,3.84l7.86,1.08l0.78,4.4l1.08,1.27l-2.22,1.74l-6.98,0.11l-6.09,1.79l-2.61,2.17l2.42,-0.1l2.23,3.81l1.98,-0.52l1.29,4.09l0,0l-0.02,0.26l0,0l-3.08,2.67l0.59,1.1l-1.02,1.2l3.28,0.37l0.58,1.28l1.62,0.56l-0.02,1.22l-1.25,0.86l0,0l-2.25,-0.71l-2.86,-2.72l-1.24,3.08l-1.88,-0.67l-1.35,0.81l0.56,4.51l-4.07,1.85l-0.37,-6.31l-1.3,-0.47l-2.2,1.52l0.55,0.76l-0.59,1.34l1.56,2.36l-0.42,1.08l-0.69,-0.17l1.88,2.72l-1.8,0.39l-0.14,0.83l-4.75,-0.84l-1,-0.97l-1.48,0.38l-0.98,-2.69l-3.18,-0.12l-0.24,-1.39l-2.99,-0.48l-0.65,-1.77l-1.06,-0.18l-1.56,-2.18l0,0l-3.07,-3.98l-8.07,-2.34l3.36,-2.17l-0.21,-5.83l-1.67,-0.36l-3.54,2.18l-3.26,-2.68l-1.74,0.1l-0.6,1.12l-4.7,-1.94l1.5,-1.12l0.12,-1.81l-2.23,-2.2l4.18,-3.01l0.02,-1.49l0,0l1.43,-1.77l0.9,1.27l3.5,-0.29l0.78,-3.38l1.63,0.13l0.59,1.19l2.49,-1.11l-0.17,1.08l1.06,0.32l0.26,-1.95l2.46,-0.69l-0.11,-3.76l0.8,-1.06L128.02,219.89z" />
            <!-- <text class="numero-dep" text-anchor="middle" xlink:href="#path-44" x="142" y="242">44</text>-->
          </a>
    
          <a xlink:href="" xlink:title="Loiret" id="FR-45">
            <path class="land"
              d="M297.89,179.38L299.84,179.51L302.49,178.11L304.93,178.63L304.44,177.54L305.67,177.83L306.57,175.79L308.34,176.55L308.32,178.63L309.22,177.59L310.59,177.81L310.59,177.81L311.05,176.75L311.05,176.75L311.81,176.5L313.05,178.05L314.93,177.56L314.93,177.56L315.74,181.04L319.15,182.38L319.86,184.74L319.18,187.44L318.21,186.93L316.57,189.46L319.8,189.44L321.37,188.4L327.44,189.49L329.46,188.13L328.72,186.8L329.82,187.63L331.32,186.68L331.41,188.98L332.17,189.09L334.11,187.46L337.05,187.03L337.05,187.03L340.89,188.88L340.33,189.95L341.15,189.89L341.73,192.6L344.84,195.29L343.79,195.93L344.86,196.72L344.98,198.82L343.63,200.35L343.63,200.35L343.14,200.69L343.14,200.69L341.75,202.64L340.05,202.94L340.2,204.95L339.49,205.34L341.02,206.42L340.21,207.42L340.72,210.3L333.51,212.12L333.35,214.61L334.62,214.4L334.64,215.43L336.6,216.77L336.3,217.59L337.73,219.03L336.86,220.17L337.03,221.87L338.69,223.68L338.69,223.68L337.95,224.44L336.13,223.9L334.39,225.18L333.72,224.72L333.33,225.53L334.45,226.65L334.45,226.65L331.65,228.23L328.52,225.57L328.22,228.27L325.88,228.89L325.4,227.32L323.67,226.46L323.75,225.42L321.52,223.52L318.54,223.75L316.32,221.21L313.59,222.69L310.17,220.02L308.06,220.56L308.06,220.56L308.15,219.29L306.58,217L302.81,217.56L298.51,216.99L296.67,218.31L295.71,218.2L295.53,216.9L292.59,217.21L292.29,216.15L291.81,218.23L289.6,219.56L287.38,218.09L286.27,213.61L282.82,212.07L280.65,214.08L281.58,213.09L279.38,211.39L280.31,209.69L278.48,208.25L281.19,204.65L280.81,203.19L278.35,201.38L279.69,200.08L280.1,197.8L278.28,198.27L278.28,198.27L277.9,195.49L279.41,195.32L279.3,194.44L281.33,195.27L281.53,194.45L282.4,194.54L282.57,192.82L284.01,192.41L284.46,193.26L285.22,192.35L287.55,193.22L289.74,192.39L289.93,191.57L291.04,192.25L293.28,191.09L294.79,188.09L294.14,187.24L294.96,186.38L296.98,186.41L296.03,185.07L296.69,184.63L296.95,185.29L297.28,184.54L296.56,181.91z" />
          </a>
    
        </g>
      </svg>
    
    </div>

    In the example I've appended the text to the svg element but you may choose to create a different group for the text or appending it to the <a> parent.