Tuesday, October 23, 2018

Continent Maps (Part 1): SVG and Coordinate Systems

Up until now, the size of maps in Dragons Abound have been fixed and somewhat indeterminate.  They are what I think of as “regional" scale maps -- not a whole world map, but a significant chunk, like the west coast of the United States, or a part of Europe.  I like this scale quite a bit, but I want to do some experimenting with Dragons Abound to see if I can generate world-sized maps (or at least bigger than the regional maps I currently generate).  But before I start into that, a little digression on fantasy world maps.

When you stop to think about it, the world is a big place.  Most fantasy “world" maps are nowhere near the proper size.  Consider, for example, Middle-Earth, the setting of Lord of the Rings:

While seemingly depicting a huge world, in fact Middle Earth is based upon Europe:
which means a real “world" map of Tolkien's world would be about 50x the size of the Middle Earth map (!).  In fact, most fantasy world maps I see depict what seems to be about a continent or so of the world:
That seems to be about the biggest area that depicts well in fantasy map style.

So generating a real “world map" is probably too ambitious.  A better target would be a continent (or partial continent) sized map.  (Although it's convenient to still think of this as “world" sized.)  So how big a map would that be? If the current Dragons Abound maps are “sub-continent" size, this suggests that I might want to be generating maps that are 8-10x as large.

Before I jump into the challenge of generating bigger maps, I need to have a better understanding of the different coordinate systems Dragons Abound uses.  I inherited multiple coordinate systems from Martin O'Leary's original code, and their interactions can be confusing, even after working with them for two years.  Mostly I've gotten away with not fiddling with them, but obviously I'm going to have to do that to generate bigger maps.

To start with, the “world" behind the regional map is currently generated within a unit square.  Each regional map has locations from (-0.5, -0.5) to (0.5, 0.5) with the origin (0,0) in the middle of the region.
The one oddity here is that the Y axis is reversed from what you saw in high school geometry.  -0.5 is at the top of the map and 0.5 at the bottom.  The Y axis is often reversed in computer graphics.  I've heard this explained as an artifact of how early monitors (TVs) scanned from top to bottom, so the first scan line was at the top, the next just below it and so on, so the Y index of the scan lines went from zero at the top to some positive number at the bottom.  Whatever the reason, SVG (Scalable Vector Graphics) uses this coordinate scheme, so Dragons Abound does as well.

This coordinate system is independent of how the map will be displayed.  It's just a unitless system for constructing the world -- a city is located at (0.12875, -0.223), a border runs from (0.337, 0.010) to (0.333, 0.017), etc.  And while the current regional maps are limited to 0.5 to -0.5, that's not a limit on this coordinate system.  I can create world outside of these limits.

The next coordinate system is what SVG calls the viewbox.  This establishes the actual coordinates that will be used to draw the graphics.  For example, Dragons Abound sets the viewbox to start at (-500, -500) and have a width of 1000 and a height of 1000:
(Upon review, the number at the top of the Y axis should be -500.  Sorry for the typo.)

You'll note that with this choice, converting between the first coordinate system and the second system just involves multiplying everything by 1000.  So to draw something, Dragons Abound finds its location coordinates, multiplies by 1000, and draws to those SVG coordinates.

So I can use the viewbox coordinates to draw a line from (say) (0, 0) to (250, 250).  But I don't really want that to draw a line from (0, 0) to (250, 250) on the computer screen.  For one thing, that would mean that if I wanted to display the map at a different point on the screen, I'd have to change all the coordinates of everything on the map and redraw it.  That would be painful.

To handle where the graphics show up on the screen, there's a third coordinate system which SVG calls the viewport.  The viewport is the actual part of the page where the graphics will be drawn (on a web page, it's an <svg> element).  It has a width and a height and a location.  The location is the coordinates of the upper-left corner of the viewport.  So if I was displaying a map in a viewport at (30, 100) that had a height and width of 800, the viewport coordinate system would look like this:
In SVG, the viewbox and viewport coordinate systems are connected to each other, and SVG takes care of translating between them.  You just draw into the viewbox coordinate system, and what you draw shows up in the corresponding place in the viewport.  (There are some problems when you create a viewbox and viewport that don't have the same aspect ratio.  Then things either get clipped or stretched to fit, depending upon your choice of the preserveAspectRatio attribute.  I just recommend not doing that in the first place.) 

So to recap:  A city located in the world at (0.10, 0.33) is drawn at (100, 330) and shows up on the screen at (110, 764).

You can see why keeping track of this can be confusing!

What happens if I change each of these coordinate systems?  In the first coordinate system, suppose I generate a world that only goes from -0.25 to 0.25 in each axis.  Then the resulting world is only a quarter the size of my usual world and only fills up the middle part of the entire viewport:
(You can also see a few weird edge artifacts that are normally hidden.)  Likewise, if I double the size of the first coordinate system, there's a lot of map that isn't seen because it is outside the edge of the viewport.

What happens if I double the size of the viewbox?  Well, if I also double the ratio between first coordinate system and the viewbox (from 1000 to 2000), then nothing much happens.  If I keep the ratio at 1000, then once again the map is reduced to half size.
However, this time the map has the original area 1x1.  Again you can see edge artifacts that are normally hidden (like the bulbous sides of the forests).  You can also see that the ocean pattern is not correct -- it must have hard-coded some assumptions about the viewbox size.  Likewise, the compass seems to be finding the corner of the viewbox, rather than the corner of the map.

Conversely, if I halve the viewbox size, it has the effect of zooming in on the map:
Here we see only the middle quarter of the map.  This isn't a very useful way to zoom, because you can see that having only half the map visible creates some problems -- the marker for the city “South Owenson" is off screen, for example.  It also effectively doubles the size of fonts, etc., which I probably don't want.

A more useful aspect of viewbox is to change the origin.  So far, I have kept the viewbox centered on the map, but that's not required.  For example, I can shift the map to the right by centering the viewbox on a point on the left side of the map:
Again, you can see edge effects and some other problems, but effectively the map has been slid over to the right.  The usefulness of this may not be obvious, but suppose I generate a map that is twice the width of the normal map.  By default it looks like this:
This looks like any other map, but it's actually only the center part of a bigger map.  So I can now adjust the viewbox to bring other parts of the map into view:
Here I've slid the viewpoint to the left, so we see more of the world to the west of the original view.  Some names have changed on the map, because Dragons Abound currently does some things (like naming features) based upon whether they're visible.  I'll need to change that to keep the map consistent when sliding the viewbox around.  Eventually, though, I'll be able to slide the viewbox around a bigger map and generate regional maps of any desired area.  So I can generate and display a large continent-sized map, but I can also generate regional maps of areas within the larger map.

To summarize, then.  There are three coordinate systems in use.  The first is an abstract coordinate system for the world features.  The second is the viewbox, and it defines the portion of the world that is visible.  The third is the viewport, and that controls where on the screen the map is drawn.  To draw a bigger world, I need to expand the first coordinate system.  To show more on the screen, I need to expand the viewbox.  I can also slide the viewbox around to show different parts of a larger world.

Next time I'll work on fixing some of the inconsistencies that arise when I use the viewport to show only part of a larger map.


4 comments:

  1. "Only" three coordinate systems? I swear some of my projects at work have up to five coordinate systems, and the transforms include rotation and mirror as well. It's madness! Anyway, at least it sounds like you have a good understanding of what your coordinate systems are. It would be neat to have a full world map that you can pan and zoom in like Google Maps.

    ReplyDelete
  2. I stumbled on your blog (how has it been hidden so long?) and was struck by the quality of your rivers. Also your maps are gorgeous.

    I wrote an app (https://www.drivethrurpg.com/product/217951/Sandbox-World-Generator) to procedurally generate content for D&D 3.5 - it's not enough to say there's a kingdom there, I want to know the king's class and level, who is in his court, and how many soldiers he has and what they are armed with! But to do this I had to have a map first, so I wrote a map generator which sort of works (doesn't make realistic islands though, and my rivers are laughable).

    The app lets you zoom in and see more detail; I have dreams of an app that starts at the continental level and lets you zoom down to tactical maps of 5' squares, where every NPC has a complete statistic block and randomly generated background that reflects the NPCs around him. And in those dreams my map looks as beautiful as yours. But unfortunately I have a day job.

    If only we could get people to pay us to do the fun stuff!

    ReplyDelete
  3. Thanks for the kind comments! I've had people offer money for this, but not enough to live on :-). So I'm happy to keep working on it as a personal growth project.

    ReplyDelete
  4. Hey! I can't see that 2nd image, as I'm using Firefox and it's webp :-/

    ReplyDelete