Tuesday, September 12, 2017

Labeling the Ocean (Part Two)

Now that I have ocean labels working on my sample map, it's time to randomly generate a bunch of maps and look for problems.  Such as this:
This map has a lot of water -- enough to qualify to be an ocean -- but it's all spread around the edges and the ocean label doesn't look very good jammed up into the corner like that.  So apparently choosing to label any ocean bigger than 20% of the map isn't a good enough heuristic.  A quick solution to this is to put some minimum on the "distance to land" metric, which is this green circle:
In this case, that green circle is pretty small, so this map wouldn't get a label.  In fact, maybe that metric alone is sufficient for determining when to create an ocean label -- I can drop the 20% ocean minimum, and just label oceans when I find a big enough expanse of open sea.

Here's another problem:
Here there's sufficient space to fit an ocean label, but it's in a spot where there is land on all sides. This would be a nice location for a a label like "The Bay of Azgarth" but it's an odd location for an ocean label. The open spot up in the corner would be a better location.  I think the lesson here is to put the labels where they visually connect with a water edge of the map. The fact that the map ends on water suggests that there is no interesting land beyond that edge, i.e., that it's open ocean in that direction.

One way I can try to address this is to force candidate locations to be near to a map edge, i.e., to force the green circle to touch the edge of the map:
That looks better, but it would be nice to take the corner if it was available, even if that meant the green circle was a little smaller.  I can get this behavior by adding a bonus to spots that touch two map edges at the same time:
The bonus is visible here as the green circle being bigger than it should be (instead of stopping where it touches land, it actually overlaps a bit).  Let's see how this affects my previous test map.  Here's what it looks like without the bonus:
And here it is with the bonus:
The old placement wasn't terrible, but this seems better. Let's try some more maps:
Whoops.  The label is on the wrong side of the arc, and the arc is backwards.  This is more-or-less the same problem I faced with path labels.  The solution is basically to flip everything for certain base angles of the label:
So that fixed the label orientation, but I don't like the base angle of this label.  It does follow the major axis of the ocean (which runs from the upper left corner to the lower right ocean) but it doesn't look right to me.  After some thought, I decided that it might look better for the label to rotate around the center of the map.  This would mean the base angle would be at right angles to the line from the label to the center of the map:
That looks much better to my eye, although the label seems too close to the land.

Continuing onward:
Here's a label on the lower half of the map.  At first glance it looks okay, but then I realized that the curve of the arc should be going away from the center of the map.  It turns out that when labels are on the lower half of the map, I also have to reverse the "sweep" of the elliptical arc so that it bends the other way:
This is a different map, but you can see that the label arc now curves away from the center of the map.

One thing I've noticed looking at these sample maps is that the ocean label always moves a small distance off the original placement:
The simulated annealing algorithm will sometimes leave a label in a non-optimal spot, but this is happening consistently.  My first thought was that one of the simulated annealing criteria for area labels was causing it to move, but turning off the criteria didn't fix the problem.  This makes me suspect the label isn't really starting in the red box where it is supposed to start.  I can check this by peeking at the label right before the first iteration of simulated annealing:
The label should be square in the middle of the red box, so yes, that seems to be the problem.  Either I'm not placing the label where I think I am, or I'm not calculating the bounding box correctly.

After some investigation, the problem seems to be partly operator error on my part and partly a bug/quirk in SVG.  The error on my part is in miscalculating the Y offset for the label; I accidentally had it at twice the necessary value.  That's easily corrected, but the label is still in the wrong place in the X direction (along the arc):
I'm centering the label on the arc by setting the label's anchor to the middle of the label, and then placing the label at 50% of the way along the arc.  This should be correct, but something about the combination seems to be broken.  I can apply a manual adjustment:
This adjustment changes a little bit with the length of the label, so this won't be as accurate for a different length label, but this isn't brain surgery, so I picked a reasonable value and left it at that.

Let me go back now to the placement of the labels.  Here's an example map with a reasonable ocean and a label placed:
I think this label would look better if it were pushed out more towards the edge of the map, to better associate the label with the (unseen) ocean beyond the edge of the map.  Since I know the whole circle is only sea, I know there won't be a collision problem.  If I push it out too far and off the map, simulated annealing will pull it back in.

To do this, I'll shift the starting point of the label out along the line from center of the map to the original placement.  (Since the center of the map is (0, 0), this vector is easy to calculate from the original starting position.)  Then I can move the label outward along that vector some percentage of the radius of the green circle -- say 50% to place the label halfway between the center of the green circle and the edge of the green circle.  Here's what that looks like:

I think that looks much better.  Here's what it looks like on another test map, without the debugging clutter:
If the label is longer, it can move off the map when shifted out from the center, but the simulated annealing (label placement) pulls it back onto the map:
You can see that the red box indicating the original label placement goes off-screen.  The criteria for area labels puts a heavy penalty on going off-screen, so the algorithm finds a spot that is on-screen and as close as possible to the original placement, so essentially tight into that corner.

Generally speaking this seems to work pretty well.  Here's an example map I'm not so sure about:
I don't much like nearly-vertical labels, although this is not so different from similar labels in some of my reference maps, e.g. "Western Sea" on this map:
The problem might be that the label on my map doesn't run parallel to to the shore.  I'm going to working on creating labels parallel to the shore shortly, so maybe I'll revisit this example then.

No comments:

Post a Comment