cancel
Showing results for 
Search instead for 
Did you mean: 
Reply
Highlighted
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

Accepted Solutions
cogsie Helper I
Helper I

Re: Overlapping Tick Marks on X-Axis

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
cogsie Helper I
Helper I

Re: Overlapping Tick Marks on X-Axis

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

Super User I
Super User I

Re: Overlapping Tick Marks on X-Axis

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!




cogsie Helper I
Helper I

Re: Overlapping Tick Marks on X-Axis

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.

Super User I
Super User I

Re: Overlapping Tick Marks on X-Axis

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!




cogsie Helper I
Helper I

Re: Overlapping Tick Marks on X-Axis

@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...

cogsie Helper I
Helper I

Re: Overlapping Tick Marks on X-Axis

@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...

Super User I
Super User I

Re: Overlapping Tick Marks on X-Axis

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!




Helpful resources

Announcements
New Ranks Launched March 24th!

New Ranks Launched March 24th!

The time has come: We are finally able to share more details on the brand-new ranks coming to the Power BI Community!

‘Better Together’ Contest Finalists Announced!

‘Better Together’ Contest Finalists Announced!

Congrats to the finalists of our ‘Better Together’-themed T-shirt design contest! Click for the top entries.

Arun 'Triple A' Event Video, Q&A, and Slides

Arun 'Triple A' Event Video, Q&A, and Slides

Missed the Arun 'Triple A' event or want to revisit it? We've got you covered! Check out the video, Q&A, and slides now.

Join THE global Microsoft Power Platform event series.

Join THE global Power Platform event series.

Attend for two days of expert-led learning and innovation on topics like AI and Analytics, powered by Dynamic Communities.

Community Summit North America

Community Summit North America

Innovate, Collaborate, Grow. The top training and networking event across the globe for Microsoft Business Applications

Top Solution Authors