0

This is very special. I have some pathes (imagine them as rivers on a map) and want them to give a name.

This code is working properly. It is creating pathes and after that writing the text on thos lines:

function draw_rivertext(){
    var featureCollection = topojson.feature(currentMap, currentMap.objects.text);
    svgmap.append("g")
          .selectAll("path")
          .data(featureCollection.features)
          .enter().append("path")
          .attr("d", path)
          .attr("class", "helperline")
          .attr("id", function(d) {return "path"+d.properties.id});
    svgmap.append("text")
          .selectAll("text")
          .data(featureCollection.features)
          .enter()
          .append("textPath")
          .attr("class", "helperline-text")
          .attr("xlink:href", function(d) {return "#path"+d.properties.id})
          .text(function(d) {return d.properties.name});
}

But the issue is that the text has just one font-size. So let's say I have a long river and a short. So the short river should be super small instead of the exact same huhge size.

So my idea was to set on the text:

.attr("font-size", "10")

and here the value 10 needs to be that current size from the path id. I really don't have an idea how to do that.

Here some links concerning this topic:

I also found some that are resampling the font-size and so on after creating it with jQuery. Well that would be for sure a solution. Getting the path sizes; resampling the text attributes on that values. But I don't think that's a proper solution.

Another idea could also be to specify the font-size in the topojson as an own parameter. But ohh lord this would be try and error as there is no real measuring in QGIS for this.

Community
  • 1
  • 1
kwoxer
  • 3,734
  • 4
  • 40
  • 70
  • 1
    Get the length of the drawn path and convert that to a font size, maybe with one of D3's scales. – Lars Kotthoff Jan 27 '15 at 19:01
  • Lars, could you maybe describe it a little bit more? – kwoxer Jan 27 '15 at 19:03
  • 1
    Well, use `.getTotalLength()` on the `path` node to get its length (or use the bounding box instead if that's more suitable), then convert this value to a font size. – Lars Kotthoff Jan 27 '15 at 19:15
  • Another issue is that I have curved paths. So I think a box would not be that good. I try some things out and come back. – kwoxer Jan 27 '15 at 19:17
  • I cannot get it to work somehow. It always says "TypeError: path.getTotalLength is not a function" – kwoxer Jan 27 '15 at 20:08
  • 1
    If `path` is your selection containing one path, use `path.node().getTotalLength()`. It's not a D3 but a plain Javascript function. – Lars Kotthoff Jan 27 '15 at 20:32
  • Indeed with that way it works. But the problem is that there is more than one path. Do you also know how I could get this into a legal expression: .text(d3.select(function(d) {return "#path"+d.properties.id}).node().getTotalLength()); – kwoxer Jan 27 '15 at 20:50
  • I mean var onepath = d3.select("#path1"); and then... .text(onepath.node().getTotalLength()); works. – kwoxer Jan 27 '15 at 20:50
  • Another way would be to create x/y/viewbox when I create the pathes. But also no clue how to do that. That way I could simply catch the size from there. – kwoxer Jan 27 '15 at 20:54
  • Now I tried var onepath = d3.select("#helperpath"); onepath.selectAll("path") .attr("x",self.node().getTotalLength()); but I don't know how to replace self so that it is the current path. – kwoxer Jan 28 '15 at 07:22
  • 1
    Use `.each()`: `selection.each(function() { this.getTotalLength(); })` – Lars Kotthoff Jan 28 '15 at 09:32
  • var allpathes = d3.select("#helperpath"); allpathes.selectAll("path").attr("x",allpathes.each(function() { this.getTotalLength(); })); ... but then I get TypeError: this.getTotalLength is not a function. Could you tell me what selection is? – kwoxer Jan 28 '15 at 09:45
  • 1
    E.g. `d3.selectAll("path")`. You can't use that inside an attribute definition. – Lars Kotthoff Jan 28 '15 at 09:49
  • But that does not really help. I have one id for the path information. And another for the text. I have to selectAll the text and then grab information from the pathes. So I need to combine it, don't I? – kwoxer Jan 28 '15 at 09:53
  • Append the paths first, then go through them with `.each()` and add the length to the data bound to them. Then append the text (which will propagate the data) and use the length you've added to the data before to figure out the font size. – Lars Kotthoff Jan 28 '15 at 09:58
  • I have created an example with some lines and text on it. Could please show me on that example how you use the each() function. Many thanks in advance. http://jsfiddle.net/2a5syp0j/2/ – kwoxer Jan 28 '15 at 10:45
  • `d3.selectAll("path").each(function() { this.__data__.pathLength = this.getTotalLength(); });` – Lars Kotthoff Jan 28 '15 at 11:28
  • Well I think I have already got enough help from you, but I really don't know where to place it, could you create a new version on my fiddle please? Would really appreciate it Lars =) Thanks – kwoxer Jan 28 '15 at 11:40
  • 1
    Well the fiddle doesn't do anything, so I'm not sure how useful adding more code to the broken code would be :) Could you put up a working version of what you're trying to achieve please? – Lars Kotthoff Jan 28 '15 at 12:03
  • Ohh I didn't know it's not working, sorry. But still working here. Using Firefox on http://jsfiddle.net/kwoxer/2a5syp0j/2/ Also checked with Chrome, working, too. – kwoxer Jan 28 '15 at 12:07
  • 1
    Ok, now it's working. See http://jsfiddle.net/2a5syp0j/3/ – Lars Kotthoff Jan 28 '15 at 12:15
  • Error 500 WOW! Server Error. The email has been sent to the admin. =/ – kwoxer Jan 28 '15 at 12:16
  • 1
    Yeah I got something similar with your fiddle earlier. Still works for me, maybe if you give it a few minutes... – Lars Kotthoff Jan 28 '15 at 12:20
  • Ok works now. Thank you =) – kwoxer Jan 28 '15 at 12:31
  • Ok, just some notes on this. Working in Firefox, showing the text in Chrome but now I have some side-effect like underlaying stuff in not click-able, and in IE there are 3 texts shown, and the crazy thing is that in the 3. there is just written the first 3 chars of the words. Can help me any further on this? Do you need a jsfiddle? – kwoxer Jan 28 '15 at 22:45
  • I think this is going quite a bit outside the scope of the question :) I am available for consulting though, so feel free to drop me an email. – Lars Kotthoff Jan 29 '15 at 09:23
  • Can we maybe also use Skype or ICQ? =) – kwoxer Jan 29 '15 at 09:29
  • Again, better discussed via email :) – Lars Kotthoff Jan 29 '15 at 09:29

1 Answers1

0

Thanks to Lars Kotthoff this is a working solution:

svgmap.append("g")
          .selectAll("path")
          .data(featureCollection.features)
          .enter().append("path")
          .attr("d", path)
          .attr("class", "helperline")
          .attr("id", function(d) {return "path"+d.properties.id})
          .each(function() { this.__data__.totalLength = this.getTotalLength(); });
    svgmap.append("text")
          .selectAll("text")
          .data(featureCollection.features)
          .enter()
          .append("textPath")
          .attr("class", "helperline-text")
          .attr("xlink:href", function(d) {return "#path"+d.properties.id})
          .attr("font-size", function(d) { return (d.totalLength/8); })
          .text(function(d) {return d.properties.name});

Dividing by 8 works pretty well. The only issue now is that some text is not fitting, but 90% is looking great and the colculated size works great.

For my cases I decided to calculate the a size on each element by myself. So this here is way easier but maybe not the perfect solution.

kwoxer
  • 3,734
  • 4
  • 40
  • 70