Tuesday, January 30, 2018

City Symbols (Part 9): Using City Icons on a Map

In the previous posting, I finished up creating the settings to make city icons like those used on the Skies of Fire map:
Now I'm going to talk about using these on a map.

Currently, Dragons Abound marks the locations of cities with circles:

Different size cities are indicated with different size and style of circles.  This is a very common map style, and has a lot of advantages when on procedurally generated maps.  It's easy to center the circles on a location, easy to calculate how close the label is to the circle, and it looks fine even when it overlaps the ocean or a map feature (like the symbols for "Bi en I" and "Ao Oiru" do in the map above).  

The procedurally-generation city icons, on the other hand, are relatively large and oddly shaped.  Sometimes they look fine on the map, as with these examples:
But often there are problems, as with these examples:

The icon in the upper left is floating out to sea (or more generally, interacting in a bad way with another map feature) while the icon in the lower right is colliding with it's own label.  The latter problem is a simple fix.  In the quick hack I did to add these icons to to the map, the labeling routine doesn't know that the icon is bigger than the small circle it expects, so I just have to add in a more correct size:
The other problem is more difficult.  The city icons are large and not uniformly shaped, so it seems inevitable that these will sometimes collide with other map elements in an unwelcome way.

However, I have one advantage that a real cartographer doesn't.  My maps don't have to reflect "reality."  Since I'm generating the entire map, I can choose not to generate a city in a place where the city icon would look bad!  There are a couple of ways to do this.

One approach would be to simply eliminate any city that had an icon problem.  But that might eliminate too many of the cities -- I don't want to unwittingly depopulate the map.

Another approach is to have rules during city generation that would avoid locations where the icon would look bad.  The difficulty with this approach is that at the time I'm generating the city locations I don't know how I'm going to be displaying them -- what kinds of icons or the sizes and shapes of the icons.  (And I want to keep generation and display separate.)  So the rules would need to be very general and conservative to work well. In the case of "Chuskoon" above I'd need to have a rule that keeps city locations far enough away from the coast that the biggest possible icon wouldn't stick out to sea.

Another approach is to wait until I'm going to display the cities, and then move any city location that is causing a problem.  In the case of "Chuskoon" above I'd slide the city location to the left until the actual icon I'm using no longer overlapped the ocean.  This would result in the minimal "fix" for each problematic city.  The challenge with this approach is that moving the city location might break other map features.  For example, if I've drawn a road between Chuskoon and another city, the road might now go to the wrong location.

Considering all these approaches, I'm inclined to use the last.  My intuition is that most fixes will only require moving the city a short distance, so the impact of moving the city should be minimal.  Also, with the relatively large icons, there's a good chance that the original location of the city will still be somewhere under the icon.  It doesn't really matter if a road comes into the Chuskoon icon at the lower left or the lower right.  So this should also help minimize this problem.

The question now is how to move the city location to fix any problems.  We have a desired location for the city location (the original location) and we want to find the closest nearby spot that meets the other criteria (not overlapping a map feature, etc.)...  Hey, this is sounding a lot like another problem!

In fact, this is the same as trying to find a good label location (although city locations will have different success criteria).  And there's no reason I can't run city locations at the same time as the label locations.  I'll just treat the city icons as "labels" that need to be placed on the map, with a strong preference to being near their original location.

So what criteria should I use when placing the city icons?  Consider these examples:
Here I have cases of city icons overlapping rivers, mountains and coastlines.  Most of these look fine to me.  The 7 building city near the bottom middle looks a little awkward on the river, but the other cities straddling rivers don't look as awkward.  A river passing behind a city (as with the other two cities at the bottom of the image) looks fine.  The icons that overlap upward into the ocean look fine.  The middle upper icon that overlaps a bay looks a little confusing, but is probably acceptable.

Here are some examples that don't work as well:
In each of these cases, part of the baseline of the icon is in the ocean.  It looks okay if the icon projects upward into the ocean, but it's not okay if the icon looks as if it is sitting in the ocean.

The reference map mostly follows similar rules, but breaks them in a few places:
In this excerpt there are icons overlapping mountains and rivers, as well as sticking up into the ocean.  But while most of the icons are placed to stay out of the ocean, a few like "Port Turio" have a foot in the ocean.

So it would appear that the most important criteria is to place the icon so that the baseline of the icon is entirely on land, while staying as close as possible to the original city location.  I'll also add a criteria to keep the icon on the screen.   After implementing that (and a frustrating couple of days debugging) I have this:
You can see that all of the cities have migrated onto the land, where they're generally snugged up against the coastline near the original city location.

One subtle problem this introduces is in the label for the city.  Now that cities can move around the map, the city labels need to follow them -- I can no longer tie the label to the original static city position.  For example, in the above map, you can see that "Nuochnir" has been placed close to the city's original location rather than it's final location.  To make the labels follow the cities around, I have to link the labels to the icons rather than a static point:
And now "Nuochnir" and "Nochboch" have migrated to better locations.

Here's what a full map looks like (click through for full-sized version):

Wednesday, January 24, 2018

City Symbols (Part 8): Recreating the "Skies of Fire" Style

There's more to be done on building styles, but at this point I have the basic functionality to recreate icons as used on one of my example maps:
Generally speaking there are two types of icons on the map:  city icons and fort icons.  The only significant difference between the types is that the city icons have peaked roofs and the fort icons have battlements.

I'll start by duplicating the look of a single building.  I'll set the fill color of the building to red and the roof color to black.  I'll use the simple "peaked" roof type and fill the roof with black.  I'll also adjust the size and width/height ratio to match.  I've isolated a few icons from the map for comparison:
I've shown the icons at both 250% and actual size.  The icons from the reference map are in the first column and my icons in the second column.  It's a pretty good match, although the outline is heavier on the map icons.  That's easily adjusted:
Now let me work on combining buildings to create icons.

The icons are essentially a line of buildings with some constraints.  Icons for small cities have two buildings; icons for medium-sized cities have five buildings; and icons for the largest size city (there is only one) have seven buildings.  In small cities there are two and three story buildings; in larger cities there are also four story buildings.  Adjacent buildings are never the same number of stories.  All the buildings in an icon have the same baseline, but adjacent buildings overlap by a small amount.

Combining buildings into an icon is basically just a matter of drawing multiple buildings.  But since I'm creating these buildings procedurally, I don't know everything about them ahead of time.  That creates some challenges with placing the buildings -- if I don't know how wide a building will be until I create it, then I don't know ahead of time where to place it to get (for example) a certain overlap with the next building.  Likewise, if I want to center the icon on a particular spot, I can't do that until I generate the entire icon and know it's width and height.  In general, I address these challenges by first creating the building or icon and then moving it to where it needs to be.  Fortunately this is fairly easy to do with an SVG transform.

But for the moment I'll just work on generating an icon that looks right.  Implementing the rules above gives me this first cut:
Here the icons from the reference map are on the left and my generated ones to the right.  Here's the same thing blown up a bit to make it a little easier to inspect:
I see a couple of problems.  First, the reference icons have wider buildings than my icons.  That's just an adjustment to the height/width ratio of my buildings.  Second, the reference icons also have more overlap between the buildings.  (I'm also overlapping strictly left-to-right, but I'll address that next.)  Again, that's just tweaking a constant.  Third, some of the example buildings have the "Witches Hat" roofs.  I'm not sure that's terribly important, but I do like the notion of having some occasional buildings that "break the style".  I'm not going to implement a Witches Hat roof, but I can use a pagoda roof to get a similar look (especially at map scale).  Fourth, I have some buildings with no windows or doors; that looks a little odd in this style of icon.  Finally, the example icons have some faux 3D shading as if they were round buildings.  Normally if I made my buildings round they would have curved bottom edges, but if I set the curvature to zero I get "round" buildings with flat bottoms, and then I can add in some shading.
And here it is blown up a bit:
That looks pretty close.  So now let me fix the overlapping to be less regular.

As with most computer graphics, the buildings in these icons are overlapped in the order they're drawn.  I draw them left to right, so each building overlaps the building to its left.  To make that overlapping random, I need to draw the buildings in a random order.  But that's a bit problematic because I don't know, for example, how far building #1 should be from building #5 until I've drawn the buildings in-between.  However, in SVG it's possible to reorder elements even after they've been drawn.  So after I create the icon I can go through and randomly raise some of the buildings to the top.
One thing I haven't done is add the small circle at the bottom of the icon to indicate the city's map location, so let's do that:
I think that's a pretty good recreation of the original map icons.  (I don't use fort icons on my maps (yet?) but generating fort icons is mostly just a matter of switching the roof type to "battlements".)

One enhancement that I see used on some maps is to add a heavier border around the outside of the icon.  To do this, I have to take the union of all the polygons that make up the icon and then draw the union in a heavier pen:
This makes the graphic a little more recognizable as an icon and also helps separate it out from the background.

Next time, I'll start looking at using these on a map.

Monday, January 15, 2018

City Symbols (Part 7): Faux 3D Redux

In a previous posting when I added faux 3D effects to simple houses, I didn't yet have a battlement roof like these:
So now let me add faux 3D effects to these kinds of roofs.  In my last posting, I fixed up round houses by using ellipses in the faux 3D view:
If I make the battlements follow the ellipse line and fill them with the same gradient, the front of the building looks fine.  Now I need to fix up the back of the battlement roof.  In the above picture, I've drawn in the basic ellipse for the back half of the roof.  Now I need to add battlements to that line.  This is basically the same as with the front part of the roof.
Here I've added battlements to the rear line.  But there are a couple of problems.  First of all, the gaps in the battlements (which are called crenels; the solid parts are called merlons) tend to line up between the front and rear since the two lines are the same length and the pattern is the same.  Second, since I'm forcing merlons at the sides of the building, I end up with a double-wide merlon at each side.  The solution is to reverse the "polarity" of the rear battlements and make it start with a crenel (gap) at either side:
Now I need to color in the back part of the roof.  Because the back is concave, it gets the opposite gradient from the convex front part.  There's no simple way in SVG to reverse a gradient, so I have to create a new reversed gradient myself.
There are still a couple of shortcomings here.  The roof is probably too "deep" -- we should be able to see a floor between the front and back.  And the walls themselves are just lines -- there's no depth to the battlements.  Neither problem shows up at map scale, though, so I can ignore them. :-)

The parapet style roof is like the battlement style, but it sticks out from the building.  (So that defenders can drop burning oil and the like on anyone at the walls of the building.)  Drawing this in faux 3D is much the same as for battlements.  The two differences are that I draw a line around the bottom of the parapet and I add a shadow underneath the parapet.
The shadow underneath the parapet is probably overkill at the map scale but I left it in anyway.

Doing battlements on square buildings turned out to be more challenging because there are three pieces to deal with.  There are left and right sides with slanted roof lines that are in the shadows and then a back wall that is lit.  With a routine to take a line and add crenels to it to create battlements, it's mostly a matter of bookkeeping to determine the four lines of the roof, the fill areas and sorting out the order to draw them.  That only took me a few hours :-).
Surprisingly enough (to me, anyway) parapets on square buildings fell into place without any additional work.
And here's what that looks like put together to create a "castle":
Note that the wall -- since it's really just a narrow building -- has picked up a second set of battlements, but the door remains just a piercing through the front of the wall.

Tuesday, January 9, 2018

City Symbols (Part 6): Fixing Some Problems

I'm going to pause development of the city symbols for a posting in order to address a couple of problems in the current implementation.



The first problem has to do with drawing lines.  Some time ago, I switched to a new method for drawing lines.  Rather than draw a line the traditional way (by moving a "pen" along a path), I draw a line by creating a polygon outline around the path, like this:

This has the advantage that I can change the width of the line.  In the example above, the width goes from near zero on the left to wide on the right.  I can then draw the line in a pleasing way by running a smooth curve (e.g., a series of Bezier curves) around the outline of the polygon.

Generally speaking this works very nicely and is a big improvement.  However, it sometimes runs into problems at the ends of the lines.  In the example above, notice that at the right end of the line, there are two sharp right-angle turns as the polygon goes around the end of the line.  If I'm drawing the outline of the polygon with straight lines this isn't a problem.  But if I'm using some sort of smooth curve, the curve might have to distort quite a bit to go smoothly around these turns.  This results in a bulge at the end of the line, as with this example:
Here I'm drawing an elliptical arc from left to right, and where the outline goes around the sharp turns the curve bulges out to smooth those angles.

Initially I addressed this by using straight lines to draw the polygon around the line.  As long as I use enough segments in the line, this looks fine, but that does eliminate one of the advantages of drawing lines this way.  

Somewhat belatedly, I realized that I don't have to use the same curve to draw the ends of the lines as I do for the rest of lines.  At the SVG level where the line is actually being drawn, I just need a connected path around the line; it doesn't all have to be similar curves.  So in theory I should be able to switch to a straight line for the small segment at the end of the line.

D3 -- which I use for most of my graphics -- doesn't seem to have a way to switch curves in the middle of a path without sticking in an extraneous M(ove) command.  (It might be possible to implement this form of line drawing with D3 Areas but it doesn't look like that's exactly right for arbitrary lines.)  So I have to generate the paths for the two sides of the line and the two ends separately and then stitch them together manually.  That's not terrible difficult to do:
And voila!  The problem is fixed.  This method creates a flat end to the line.  That can create an issue where two lines join, as is (barely) visible in this highly magnified example:
This often looks better if the lines have rounded ends.  Naively, you might think that this just requires connecting the two sides of the line with a semicircle rather than a straight line.  But doing that would actually cause the line to extend slightly (by the radius of the semicircle) past its endpoint.  To do this correctly, I have to back off the endpoint of the line by the radius of the semicircle and then connect it with a semicircle.  I'll leave this on the TODO list until it looks like I really need that capability.



The second problem has to do with how I'm drawing round houses like these:
I'm using a quadratic Bezier curve for the round parts of these houses.  This looks okay by itself, but a problem becomes apparent when I try to draw in the back half of one of these roofs:
The outline of the roof is too pointy at the ends -- it's a football when it should be an ellipse.  I should be drawing a (half-)ellipse for the round houses instead of the curve I'm using.

The curve-creating function I'm using is very handy because it allows me to specify the starting and ending points of a curve, and the height it should curve out at the midpoint and then step along the curve generating points at regular intervals.  Doing the same thing for an arbitrary ellipse turns out to be considerably more complex, but the case I need is a little simpler.  In my case, the ellipse is axis-aligned.  Consulting the Wolfram Alpha ellipse page, I find a parametric equation for points on an ellipse that looks like this:

`x = a*cos(t)+C_x`
`y = b*sin(t)+C_y`

In these equations a and b are the X and Y axises of the ellipse, t is an angle from the center of the ellipse, and `C_x` and `C_y` are the coordinates of the center of the ellipse.  Using this equation I can write a curve-creating function that produce half-ellipses between two axis-aligned points.  Substituting the new function into my building-drawing code gives me this:
That looks a little better.  Next time I'll fix up the faux 3D view for buildings with battlements and parapets.