Example: Tanaka Contours

Also known as illuminated contours, Tanaka contours give the appearance of three dimensionality to contour lines by brightening lines on a slope facing a presumed light source while darkening lines on a slope facing away from the light source.  Lines are also made wider when perpendicular to the light source.  This topic shows how to create the Tanaka effect in contour lines.



Tech tip:  The technique shown in this topic is highly dependent upon zoom to get the desired visual effect.   To see the effect well on-screen we must zoom in to where averaging effects caused by very many small line segments do not interfere.  For the time being, the Tanaka method works best for illustrating contours when the resulting displays are used within a layout and saved as a PDF.  The illustration above, for example, is a screen shot of a PDF.   Manifold intends to tune rendering of many small line segments so the on-screen effect is as good as what appears in PDF regardless of zoom.


Please begin by reviewing the Example: Contour Areas and Contour Lines topic to learn how to create contour lines.



When we use the Contour Lines transform template we create line objects at the desired contour intervals.  Each contour line is a single line object.    The Tanaka effect depends upon coloring a line differently at different locations on the line, and also drawing the line thicker or thinner at different locations.  However, the Style pane applies color and line width to the entire line object.  How can we vary the color and the line width for a contour line?


We do that by using the Decompose to Segments transform template, which takes each line object and splits it into very many line objects, each of which is one segment of the original line.  We can then color each individual small line object a different color, from light to dark, and we can alter the width of each small line object as well.   Because all of the segments will remain lined up as they were in the original line, the resulting effect gives the illusion of a single line that varies in color and width.


To assign colors and widths, we will create a computed field that calculates the azimuth, that is, the bearing of each line segment.  Based on that bearing we can use Style to thematically format each segment so it has the color and width required for that bearing to create the Tanaka effect.


Creating Tanaka contours by decomposing contour lines into a much greater number of tiny line objects and then coloring those very many line objects by their bearings is the traditional way the Tanaka effect is created in various GIS packages.  Although it is a "quick and dirty" way of forcibly creating a very pleasing visual effect, from a purely technical perspective It is basically a highly inefficient hack: converting a single line into very many smaller line objects is very inefficient from a database perspective, and the resulting many objects are not in useful form for data analytics.   However, since the method shows the power of various Manifold capabilities used in combination it provides a useful example.

About Lines

To understand how the Tanaka effect is created we will simplify illustrations to use only a single line.    First, we will take a moment for a quick refresher on lines in Manifold, as discussed in the Drawings topic.



Consider the above line, which forms a closed figure as contour lines normally do:  the line ends at the same location where it starts.  Our example line is a very simple line that consists of only eight straight line segments, so it does not appear like a smooth curve.   Contour lines normally consist of very many more segments so they appear to be smoothly curved.    See the discussion in the Drawings topic for illustrations how using very many segments can make a line appear to be a smooth curve even though in reality it is made up of straight line segments.



If we open the table for our line drawing we see there is only one record, for the single line object in our drawing.   All of the coordinates for the line object are stored in the geom for that single record.



We can see the coordinates which make up that line by Alt-clicking the line to pick it, which automatically pops open the Info pane, and then either clicking any vertex or segment, or clicking the Coordinates tab in the Info pane.  That puts the line into coordinate editing mode: each coordinate appears as a small box, with a larger box at the first coordinate.



The Coordinates tab for the Info pane shows the list of coordinates that make up the line.



Lines in Manifold, as with other GIS packages, have directionality that is implied by the sequence of coordinates that define them.    When created from closed figures such as areas or as a result of contour creation, the coordinates will define the line in a clockwise sequence.    We will exploit that directionality when coloring line segments, since in the case of contour lines we can infer from directionality on which side of a hill a given segment is located.

Decompose Contour Lines into Segments

With the focus on the Line drawing, in the Transform pane we choose the Geom field, and then we double-click the Split template to launch it in the Transform pane.



In the Split template, from the pull down menu for the Split into box we choose segments.   The only Result option is (new table).   We specify the name Line Segments for the name of the new drawing.  As we enter the name for the drawing, the pane will automatically fill in an analogous name for the table.   We can change that if we like.


Press Transform.


A new Line Segments drawing and its table appears in the Project pane.



We drag and drop the new Line Segments drawing into the map, turning off the line layer.   To create the illustration above we have individually moved each segment, as described in the Editing Drawings topic, so that all of the line objects have been shifted away from each other.  That allows us more clearly to see they are all individual line objects.



Opening the Line Segments Table we see that the single line has, indeed been decomposed into eight new line objects.  Each of those line objects is a straight line segment defined by only two coordinates, the start and end coordinate of the segment.

Create a Computed Azimuth Field

We must now add a computed field to the drawing's table that gives the bearing, that is, the azimuth, of each line.   We can then use a thematic format in Style to color each line based on the azimuth of that line.



Each line is a single, straight line segment defined by only two coordinates, (x1, y1) and (x2, y2), so calculating the bearing is easy.  If we do not remember the formula we can look it up online.   Using the SQL Function Atan2, the clockwise bearing for a straight line between two points at (x1, y1) and (x2, y2) is given by:


Atan2(x2-x1, y2-y1)


The clockwise bearing provides compass degree bearings as originally used by Tanaka.   To get counterclockwise bearings (as used by programmers) the formula is Atan2 (y2-y1, x2-x1).   The Atan2 function returns a value in radians, so to get the value in degrees we multiply by 57.2958:


57.2958 * Atan2(x2-x1, x2-y1)


It is easy to extract the X and Y values for a vertex from a geom.    We can use the expressions shown as examples in the Example: Create a Geocoded Table from a Drawing topic.   Copying and pasting we can use the following:


For x2:  VectorValue(GeomCoordXY([Geom], 1),0)


For x1:  VectorValue(GeomCoordXY([Geom], 0),0)


For y2:  VectorValue(GeomCoordXY([Geom], 1),1)


For y1:  VectorValue(GeomCoordXY([Geom], 0),1)


Putting the above all together, the expression which we would write to get the bearing in degrees of any given line segment would be:


57.2958 * Atan2((VectorValue(GeomCoordXY([Geom], 1),0) - VectorValue(GeomCoordXY([Geom], 0),0)), (VectorValue(GeomCoordXY([Geom], 1),1) - VectorValue(GeomCoordXY([Geom], 0),1)))


Clearly, Copy and Paste are our friends!



To add a new, computed field to our table we click on the Line Segments Table and choose Edit - Schema.    In the Schema dialog we click the <new field> command.



In the Schema dialog, press the Add command button.



Choose Field in the drop down menu.



In the Field dialog, we enter azi as the Field name, and we choose float64 as the Type.   This will be a computed field, so we click the Edit Expression button to launch the expression builder, so we can specify the expression that will be used to compute the field.



In the Expression dialog we enter the very long expression which calculates the bearing: 


57.2958 * Atan2((VectorValue(GeomCoordXY([Geom], 1),0) - VectorValue(GeomCoordXY([Geom], 0),0)), (VectorValue(GeomCoordXY([Geom], 1),1) - VectorValue(GeomCoordXY([Geom], 0),1)))


The dialog allows free use of white space to format lengthy or complex expressions for better legibility.   We press OK.



Back in the Field dialog, we normally would check our work to make sure we entered the expression correctly.  In this case, we trust the results of Copy and Paste and just press OK.



The new azi field appears as a computed field in provisional, bluish color.  If we want to see the entire expression, we can hover the mouse over the expression and it will appear in a tool tip.  We press Save Changes to commit the change to the table and to close the Schema dialog.



A new computed field called azi appears in our table.   However, this field has negative numbers in it when we would like all of our degree numbers to be in the range from 0 to 360. We can fix that by creating a second computed field that normalizes the values from +/- 0 to 180 to the range 0 to 360.    


We once again choose Edit - Schema to launch the Schema dialog.



In the Schema dialog, press the Add command button, and then choose Field in the drop down menu.



In the Field dialog, we add a new field called azimuth that is an int16.   We want our azimuths to be integers for simplicity.   This field will also be a computed field, so we press the Edit Expression button to launch the Expression dialog.



The expression we use to normalize the values is a snippet of SQL that uses the CASE SQL Operator:


CASE WHEN ([azi]< 0) THEN [azi]+360 ELSE [azi] END


We enter the expression into the dialog, formatting it neatly for legibility, and then we press OK.



Back in the Field dialog, we check the expression and then press OK.



The new azimuth field appears in provisional, bluish color in the schema.   We press Save Changes to commit changes to the table and to close the Schema dialog.



The result is a new, computed field called azimuth is added to the table.  This gives the bearing of each line segment as an integer value in degrees from 0 to 360.  


Tech Tip:  SQL experts, of course, instead of using two computed fields, the latter of which adjusts the computation of the former, would do this in a single expression for a single computed field.   However, for the purposes of clarity in this example we have used two computed fields.   That also shows how a computed field can, in turn, make computations based on other computed fields.



We can create labels using the azimuth line to add to our map, to show the bearing for each line.

Line Color by Azimuth

Now that we have the bearing for each line we can color each line based on the azimuth.    We click on the Line Segments tab to move the focus to that layer.


We will assume the bearing to the Sun is 315 degrees, the default used in hill shading in Manifold.   To achieve the Tanaka effect that means lines with a bearing of 45 degrees should be white, and lines with a bearing of 225 degrees should be black, with bearings in between interpolated from white to black and then back to white.  



The thematic format shown above will do the trick.  We have manually added intervals with color shades interpolated.  Doing so requires some tinkering at first, but then afterwards we can simply copy the format from the mfd_meta table and re-use it over and over.    Copy and Paste are our friends!   The JSON we would paste into the StyleLineColor property would be:


{ "Field": "azimuth", "Fill": "boundaverage", "Value": 12566463, "Values": { "0": 12566463, "135": 8421504, "180": 4210752, "225": 0, "270": 4210752, "315": 8421504, "360": 12566463, "45": 16777215, "90": 12566463 } }


Pressing Update Style in the above applies the desired grayscale colors from white to black to the line segments based on the value of their azimuth field:



Right away, we can see how the effect occurs.

Line Width by Azimuth

A subtlety of the Tanaka method is to vary the width of contours so that contour line segments which run in the same direction as the Sun azimuth are thinner than those which are perpendicular to the Sun azimuth.    We can vary line widths by azimuths in Style by Ctrl-clicking the line width style button to add width to the intervals.



In the Style pane above, we have used line widths from 1 to 3.


The width values used above are exaggerated widths, larger than we would use in cartography, to provide clearer illustrations in this topic.  To vary widths for actual cartography we would use fractional widths, which can be specified in the JSON string we could paste into the StyleLineSize property  for the drawing in the mfd_meta table.   A typical string might be:


{ "Field": "azimuth", "Fill": "boundaverage", "Value": 2, "Values": { "0": 2, "135": 0, "180": 2, "225": 3, "270": 2, "315": 1, "360": 0, "45": 3, "90": 2 } }


Press Update Style.



The result is that each line's width varies by the azimuth of that line.    To see the Tanaka effect we will move the lines back into position end-to-end.



We have renamed the Line Segments drawing Tanaka, and in the map above we have removed the other, unused layers.   Line segments have been moved back into position by copying and pasting geometry for each line from a backup copy of the original Line Segments drawing, using the same technique as illustrated in the Example: Repair a Wrong Edit using a Backup  topic.   Putting the line segments together shows the Tanaka effect.



To increase the effect, we can color the underlying raster image as shown in the Style: Contouring using Colors topic using a Fill method of closest lower value using palette intervals that are the same intervals as were used to create contour lines.   That will create ranges of flat color within each contour interval that match regions enclosed by contour lines.



When the new Tanaka lines we have created and styled are shown above such matching regions of color, the effect is one of flat, solid contour levels seen slightly in three dimensions.   The opening illustration in this topic uses that effect to overlay Tanaka contours in a layer above a lower raster that has been colored using a Fill method of closest lower value using palette intervals that matched the intervals used to create the contour lines.


When we apply the above workflow to drawings of contour lines, and then combine them with contour areas, we get the full effect as seen in the illustration at the start of this topic:



The Inventor - Professor Kitiro Tanaka invented the method in 1950, calling it the Relief Contour Method.  The effect was immediately popular, even though in an era before computers it was very tedious to create manually.



Seen above is a map of Japan and surrounding bathymetry said to have been created by Professor Tanaka.


A hack?  - Yes, creating Tanaka contours as described in this topic, the same method used in virtually every GIS, even when more automated than manual process shown, is indeed a hack.  An individual contour line can consist of hundreds of thousands, or even millions of segments.   The technique described in this topic could create millions of single-segment lines for each such contour line.  While it is impressive that Manifold has the performance to handle millions of such segments creating vast numbers of objects to present what in reality is nothing but a more sophisticated drop shadow or highlighting is not as elegant as accomplishing that visual effect using a style.    Manifold expects in the future to add special style capabilities which would enable such effects to be commanded with a click or two within the Style pane.    Until then, we can use the technique illustrated in this topic.


See Also



Editing Drawings


Style Pane


Transform Pane


Edit - Schema


Example: Create a Geocoded Table from a Drawing - A partner example to Example: Create a Drawing from a Geocoded Table   A geocoded table has records with a latitude and longitude for each record.   This example starts with a table for a drawing of points where the geom field in the table contains geometry information for each point.   We extract the Y and X locations for each point  from the geom field to create latitude and longitude fields in the table for each record.


Example: Repair a Wrong Edit using a Backup - How to quickly make a backup table and to then copy and paste geometry from that table to repair errors made when editing objects.  This technique is a life-saver when edits go astray.


Example: Flooded Roads - We consider a hypothetical case of a 10 meter rise in sea level in the San Francisco Bay area, and we find what highways and major roads would be flooded by such a rise.   The example uses both raster and vector data sets, combines a number of techniques and uses the Contour, Buffer, Merge, and Clip transform templates.


Example: Contour Areas and Contour Lines - In this example we use the Contour Areas transform template in the Transform pane for images to create a drawing with vector areas showing height contours at desired altitude steps.   We color the areas using the attribute fields automatically created by the template.  Next, we apply a similar procedure using the Contour Lines transform template to create a drawing with vector lines showing height contours at the desired intervals.


Example: Trace Vector Areas from Raster Pixels - This example follows the Trace Vector Areas from Raster Pixels video on the Gallery page.   We use the Trace Areas template in the Transform pane for images to create a drawing with vector areas covering regions of similarly-colored pixels.  Next, we use a simple query to add classification codes from a USGS table of classes to the resulting drawing, using a simple INNER JOIN SQL statement.