Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
cogsie
Helper I
Helper I

Overlapping Tick Marks on X-Axis

I am creating a custom visual and hvaing difficulty with the tick mark values on the x-azis.  If I shrink the visual size the labels start overlapping and looking jumbled (see picture below).  Is there anyway to prevent this?  I think it has something to do with the hideCollidedLabels method in powerbi-visuals-utils-chartutils but I can't figure out how to make it work.  Any suggestions or examples of how to fix this would be much appreciated.

 

Capture.JPG

 

1 ACCEPTED SOLUTION
cogsie
Helper I
Helper I

In case anyone runs into the same problem, this is how I solved the issue.

 

I put the creation of the X axis inside a do/while loop.  The loop begins with creating the axis then loops through each text element and gets the width of the text.  The width is added to an array from which the largest width is determined and multiplied by the total number of ticks to get an estimate of how much space all the labels would need.  This is then compared to the plot width and if it is greater (i.e. the labels require more room than is available in the plot area and are overlapping) the whole loop is rerun (and the axis recreated) with one fewer ticks until there is no overlapping.  Generally it only needs to run 5 or fewer times, but I put in an iteration counter and added it to the while condition to avoid an infinite loop.

 

I am pasting the code below in case it is helpful.  It needs to be cleaned up considerably (I have a lot of console logs to help with debugging), but here it is for what it's worth.

 

If any one has a more efficient way I would love to hear it.

 

do {

  var xAxis = d3.axisBottom(xScale)
    .tickFormat((d) => valueFormatter.format(d))
    .ticks(tickMarks);

    this.xAxisContainer
      .attr("class", "xAxis")
      .attr("transform", "translate(" + plotArea.x + "," + (plotArea.height + plotArea.y) + ")")
      .call(xAxis)
      .style("font-size", viewModel.XAxisFontSize)
      .style("font-family", viewModel.FontFamily)


      console.log("******************")
      console.log("iteration" + m)
      console.log("View Port Width:" + options.viewport.width)
      var gCounter = 0
      var g = 0
      var gArray = []
      var labelWidth = 0
      var gMax = 0
      d3.select(".xAxis").selectAll("text")
        .each(function (d,i) {
          console.log("starting")
          let textProperties: TextProperties = {
            text: valueFormatter.format(d),
            fontFamily: viewModel.FontFamily,
            fontSize: "" + viewModel.XAxisFontSize
          }
                        
          let h = textMeasurementService.measureSvgTextRect(textProperties).width
          gCounter++
          g = h + g
          gArray.push(h)
          gMax = d3.max(gArray)
          labelWidth = ((gCounter - .5) * gMax)
                        
          console.log("----------------------")
          console.log("Font Family: " + textProperties.fontFamily)
          console.log("Font Size: " + textProperties.fontSize)
          console.log("width of " + textProperties.text + ":" + h)
          console.log("Counter:" + gCounter)
          console.log("g:" + g)
          console.log("gArray: " + gArray)
          console.log("gMax: " + gMax)
          console.log("Label Width:" + labelWidth)
          console.log("Plot Width: " + plotArea.width)
         
          return(labelWidth)
        });

            
                
        --tickMarks  
        console.log("TickMarks:" + tickMarks)
        ++m
        console.log("Plot Width: " + plotArea.width)
        console.log("Label Width:" + labelWidth)
        console.log("Test: ", plotArea.width < labelWidth)
                
      } while (m<100 && plotArea.width < labelWidth)

 

View solution in original post

7 REPLIES 7
dm-p
Super User
Super User

Hi @cogsie,

Looks like I missed this one orginally, and you've solved it yourself, but I'll share my method for managing this in case you wish to have a look.

If my axis is linear, it's essentially a combination of using getRecommendedNumberOfTicksForXAxis or getRecommendedNumberOfTicksForYAxis in powerbi-visuals-utils-chartutils.

If I have a fixed width for each tick, I then use getTailoredTextOrDefault in powerbi-visuals-utils-formattingutils to make the text display an ellipsis (...) if it gets too small.

Here's an example of how if I first change the height, my linear ticks will reduce to make room, and then if I reduce the width, the category labels will truncate and then eventually disappear if there's no room to display them:

responsive.gif

The truncation of the category text will also work if the font size is adjusted too large to fit the width.

If you want to have a look through my code, you can see how I'm managing in this function here (for ticks) and this function (for labels).

Regards,

Daniel





Did I answer your question? Mark my post as a solution!

Proud to be a Super User!


My course: Introduction to Developing Power BI Visuals


On how to ask a technical question, if you really want an answer (courtesy of SQLBI)




Thanks for the feedback.  That was very helpful.  I tried using getRecommendedNumberOfTicksForXAxis but I couldn't figure out how to get it to work so I stuck with the method I used.  Not as elegant as what you suggested, but it seems to get the job done.

 

However, your suggestion for using getTailoredTextOrDefault  on categorical labels worked really well.  I was able to use it on my Y-axis so the categories don't overlap.  Thanks for sharing that one - I never would have figured that out.

 

I really appreciate the help.

 

Now I just need to figure out how to add data labels and a legend.

You're welcome! Your visual is looking great BTW 🙂

If you want to use the MS-supported tooling for legend and data labels, you can find the doc in the same library (powerbi-visuals-utils-chartuilts):

I personally haven't used the data labels tooling, but the legend works just fine. I've messed with it too much in the visual I used above for it to be a reliable example for you, but you can see how I'm using it in this visual - specifically here (adding to DOM), here (rendering) and I've created a function here to hide the legend if the container gets too small. Hopefully these may save you some time when having a go yourself.

Good luck!

Daniel





Did I answer your question? Mark my post as a solution!

Proud to be a Super User!


My course: Introduction to Developing Power BI Visuals


On how to ask a technical question, if you really want an answer (courtesy of SQLBI)




@dm-p 

The legend is giving me a lot more difficulty than I was expecting.  I am trying to just get something, anything to show up and then I'll tweak it from there. A couple days into it and it's still not working.  Quick question, for the LegendDataPoint that is require by drawLegend(), one of the required properties is "identity".  If I just put null there for the time being, will the visual still display properly?

 

I might try downloading all your code from the visual you linked to and tweak that field to see what happens...

@dm-p 

I quickly tried changing the identity property in your populateLegend function to null on your small multiple line chart custom visual and sure enough that killed the visual.  So I think that it probably my problem.  Looks like I need to create a selection ID and use the SelectionIDBuilder to get it working.  I'll see if that helps at all...

Yes - you'll need to provide a selectionId for legend items. Null used to work in older versions but stopped working when libraries got updated for latest SDK last year.




Did I answer your question? Mark my post as a solution!

Proud to be a Super User!


My course: Introduction to Developing Power BI Visuals


On how to ask a technical question, if you really want an answer (courtesy of SQLBI)




cogsie
Helper I
Helper I

In case anyone runs into the same problem, this is how I solved the issue.

 

I put the creation of the X axis inside a do/while loop.  The loop begins with creating the axis then loops through each text element and gets the width of the text.  The width is added to an array from which the largest width is determined and multiplied by the total number of ticks to get an estimate of how much space all the labels would need.  This is then compared to the plot width and if it is greater (i.e. the labels require more room than is available in the plot area and are overlapping) the whole loop is rerun (and the axis recreated) with one fewer ticks until there is no overlapping.  Generally it only needs to run 5 or fewer times, but I put in an iteration counter and added it to the while condition to avoid an infinite loop.

 

I am pasting the code below in case it is helpful.  It needs to be cleaned up considerably (I have a lot of console logs to help with debugging), but here it is for what it's worth.

 

If any one has a more efficient way I would love to hear it.

 

do {

  var xAxis = d3.axisBottom(xScale)
    .tickFormat((d) => valueFormatter.format(d))
    .ticks(tickMarks);

    this.xAxisContainer
      .attr("class", "xAxis")
      .attr("transform", "translate(" + plotArea.x + "," + (plotArea.height + plotArea.y) + ")")
      .call(xAxis)
      .style("font-size", viewModel.XAxisFontSize)
      .style("font-family", viewModel.FontFamily)


      console.log("******************")
      console.log("iteration" + m)
      console.log("View Port Width:" + options.viewport.width)
      var gCounter = 0
      var g = 0
      var gArray = []
      var labelWidth = 0
      var gMax = 0
      d3.select(".xAxis").selectAll("text")
        .each(function (d,i) {
          console.log("starting")
          let textProperties: TextProperties = {
            text: valueFormatter.format(d),
            fontFamily: viewModel.FontFamily,
            fontSize: "" + viewModel.XAxisFontSize
          }
                        
          let h = textMeasurementService.measureSvgTextRect(textProperties).width
          gCounter++
          g = h + g
          gArray.push(h)
          gMax = d3.max(gArray)
          labelWidth = ((gCounter - .5) * gMax)
                        
          console.log("----------------------")
          console.log("Font Family: " + textProperties.fontFamily)
          console.log("Font Size: " + textProperties.fontSize)
          console.log("width of " + textProperties.text + ":" + h)
          console.log("Counter:" + gCounter)
          console.log("g:" + g)
          console.log("gArray: " + gArray)
          console.log("gMax: " + gMax)
          console.log("Label Width:" + labelWidth)
          console.log("Plot Width: " + plotArea.width)
         
          return(labelWidth)
        });

            
                
        --tickMarks  
        console.log("TickMarks:" + tickMarks)
        ++m
        console.log("Plot Width: " + plotArea.width)
        console.log("Label Width:" + labelWidth)
        console.log("Test: ", plotArea.width < labelWidth)
                
      } while (m<100 && plotArea.width < labelWidth)

 

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.