terrain evolution simulator

1996 (updated : 2008.07.05)

The software is designed to model the evolution of a square piece of terrain. It uses a fractal pattern to generate the terrain, then uses some basic geographic concepts to model erosion due to rainfall.

If you're a Geography or Geology student, perhaps you'll find this useful.

I wrote this in the early 90's for the DOS platform. It requires the fastgraf code library (gods only know where you'll find a compatible version after all these years).

 by Michael Werneburg

This program develops a model terrain surface through the use of a fractal
(self-similar at all scales) framework. It also then models the pattern of
drainage which would develop on that surface, and simulates the effects of
the flowing water on the terrain surface. This results in an evolution of
the terrain surface.
 This evolution is enacted through the use of two erosional procedures,
one of which accounts for the combined effects of overland flow and rilling,
the other which accounts for the effects of channel erosion. The first of
these is a function of surface slope and slope length, while the second is a
function of slope and runoff. The equations on which the coded implementations
are based may be found in the accompanying text.
 The modelling process involves an artificial terrain 127 x 127 elements
in size. For reasons of accuracy with regards to stream modelling (see text),
the ground dimensions of these positions has been set to 10 metres by 10 metres,
resulting in an array position ground area of 100m˝. If the ground scale is to
represent real values, then the elveation values used in the model must also
represent measurable real values. The scale of the vertical range was
determined by calculating the average slope found by establishing a sound
range of values which could be consistently modelled using an integer data
structure to hold elevation values.
 This was accomplished as follows. The integer data type meant that
elevation values could range from -32535 to 32535. It was deemed that a
greater range in elevation was desirable as a greater vertical range would
result in a finer measurement of real elevation values: this finer measurement
was imperative as the rates of denudation were in the order of a few
millimetres per year. Since the model used to create the terrain elevation
values was stochastic, the range of values thus created could not be known
until a number of runs offered a good estimate (although this could be roughly
controlled by setting the vertical range of possible values for each random
 The ability to control the results of the vertical scale generation was
used with the goal of achieving an average local slope of 4o throughout the
terrain model. The value of 4o was judged as a reasonable approximation of
global average slope.
 Through the fiddling of the vertical range generation to result in an
average slope of 4o, a real elevation value of 1 mm could be assigned to each
vertical unit. This vertical value, described hereafter as the terrain
evolution simulator (TES) scale, means that a range of real elevations of
50 metres is possible.
 The process by which the evolution model was implemented is discussed
below, at the head of the declartions of those functions which implement each
step. */

// The following inclusions compile standard C library headers.

#include "tes.h"
#define toupper(c) ((c)+'A'-'a')
#define SANITY_TEST ((Y+local_DY)<127)&&((X+local_DX)<127)&&((X+local_DX)>=0)&&((Y+local_DY)>=0)
#define SANITY ((Y+(D_xy*dy))<127)&&((X+(D_xy*dx))<127)&&((X+(D_xy*dx))>=0)&&((Y+(D_xy*dy))>=0)
#define SANITY_CHECK ((Y+DY)<127)&&((X+DX)<127)&&((X+DX)>=0)&&((Y+DY)>=0)
#define XY (X*127)+Y

// The following functions are described in the code below where they are defined.

void intro(),
 legend(int green_or_grey),
 ldraw(int x, int y, int col),
 draw(int x, int y, int col);
int vari8(unsigned char scale,unsigned char midX,unsigned char midY),
 values_gen(unsigned char isit_first),

unsigned char huge *f_model; // The use of these data structures is outlined below
unsigned char huge *river_a;

int huge *terrain;
int huge *delta_z;
int huge *entrain;
int huge *lakes;
unsigned int X, // The X position within the arrays.
 Y; // The Y position within the arrays.
unsigned char ask=1, // holder for command-line driven request for no DEM output.
int highcount = -32000, // This is used to determine the highest terrain elevation.
 lowcount = 32000, // This is used to determine the lowest terrain elevation.
 A, // These four variables are used as limits for windowing procedures which are performed during
 B, // smoothing, slope calculation, aspect generation, and watershed calculation.
 tilt=0, // the terrain will be titled....
 maxx, // width of screen
 maxy; // height of screen
long avecount; // This is a holder for average slope and elevation values.

 D_type = 'O', // holder for data type recorded to and read from Idrisi DEMs on disk
 noise, // holder for user key hits, so they're out of the way
 output_h[30], // string for output of highest elevation, slope values
 output_a[30], // string for output of average elevation, slope values
 output_l[30]; // string for output of lowest elevation, slope values
float hold_v; // holder for output values.

/* This function calls the others in order of operation. It first
* initializes the arrays f_model[] and terrain[] through the initialize()
* function. It then initiates the graphics mode in run_bgi(), and generates
* the fractal surface and terrain model with the terrain_gen() function.
* These models are simultaneously draWn to screen. Following this, the
* terrain model is smoothed within the smooth_4stats() function (the name of
* this function comes from its output of the elevation high, low, and
* average values). The slope/aspect generation function values_gen() is
* then performed to create arrays holding the slope and aspect values at
* each point in the model. The initial watershed generation function
* river_gen() is then performed; the model is now ready for the erosion
* simulation. This simulation is carried out through the use of the erode()
* function. Then the graphics mode is exited and the user is alerted to the
* completion of the program. */

void main(int argc, char *argv[]) {
char yesno;
int initmode;

initmode = fg_getmode();

fg_text("M. Werneburg 1994-98", 20);

/* These next few lines determine whether the user wishes to view the optional
* introduction, export the generated terrain models at various stages
* throughout the program) to Idrisi-format DEMs, or display rivers during
* the draWing of the terrain model to screen. */

for (yesno = 1; yesno < 5; yesno++) {
 if (argv[argc-yesno][0] == ':') intro();
 if (argv[argc-yesno][0] == '-') {
 if (argv[argc-yesno][1] == 'o') ask = 0;
 else if (argv[argc-yesno][1] == 'r') rivers = 0;

fg_text ("This model is capable of generating tilted terrains. This has the effect of", 76);
fg_text ("adding a trend to the drainage pattern.", 39);
fg_text ("Do you wish to add a tilt? [y/n] ", 33);

//textcolor(8); // these textcolor routines draW the user's response in dark grey
yesno = getche();
yesno = toupper(yesno);
if (yesno == 'Y') {
 fg_text ("Please enter a tilt effect in millimetres. The terrain will be titled so", 73);
 fg_text ("that the top of the screen is raised while the bottom is lowered.", 65);
 fg_text ("Value (mm):", 11);
 scanf ("%d", &tiltz);
// cprintf ("%d", tiltz);


cprintf("You may load an Idrisi-generated Digital Elevation Model into the simulator.nr");
cprintf("This file must be of dimensions 127 x 127, and it must have been recorded innr");
cprintf("integer format. Do you wish to load a 127 x 127 DEM for simulation?[y/n]:_b");

textcolor(8); // these textcolor routines draW the user's response in dark grey
yesno = getche();
yesno = toupper(yesno);


/* The following if statement determines the course of the program as defined
* by the user's wish to read in a DEM created by the Idrisi GIS. If the
* result is that a DEM is desired, the lone function get_terrain() are
* called. Otherwise, the other set (run_bgi(), terrain_gen(),
* smooth_4stats(), and values_gen()) are run. These two courses reflect
* the two possible paths in establishing the terrain model and the
* evolution process: in the first case, values are read from the DEM, and
* the slope, aspect, and watershed values are calculated from that; in the
* second case, the fractal modelling network (see text) is first built, then
* the terrain model is built upon that, and lastly, the slope, aspect, and
* watershed values are derived. In either case the initialize() function
* is enacted first, and the values_gen() and erode() functions, which
* determine the slope, aspect, and watershed arrays in the first case, and
* perform the actual erosion simulation in thesecond. The final function
* calls (closegraph() and the farfree() functions) do the necessary wrap-up,
* first returning to text mode, then closing the dynamically allocated
* memory blocks used in the simulator.*/

//if (yesno == 'Y')
// get_terrain();
// {
// }

cprintf("The TERRAIN EVOLUTION SIMULATOR by Michael Werneburg");

/* This function initializes the arrays f_model[] and terrain[]. These arrays are used to hold the fractal modelling network and
terrain elevation values, respectively. The first array is set to 15 for all points, and the second to 0. The reasoning for this
is as follows.
The function which follows initialize() - terrain_gen() - hunts the array f_model[] for zero values in its first pass. As there
are none but that at the point (63,63) (assigned in the second last line of the function), only that position is selected in the
first iteration of the function. The terrain[] array is set to 0 so that all points in the array have no elevation value at the
outset of the terrain_gen() function.

int initialize() {
int array_place; // set memory block size to 127 by 127
f_model = (unsigned char huge*) farcalloc(16129, 1);
if (f_model == NULL) {
 printf("error creating f_model array - insufficient memory!n");

terrain = (int huge*) farcalloc(16129, 2);
if (terrain == NULL) {
 printf("error creating terrain array - insufficient memory!n");

lakes = (int huge*) farcalloc(16129, 2);
if (lakes == NULL) {
 printf("error creating lakes array - insufficient memory!n");

for(array_place = 0; array_place < 16129; array_place++)
 f_model[array_place] = 15; // flat fractal model framework
 terrain[array_place] = 0; // no terrain
 lakes[array_place] = 0; // no lakes

 // 63 * 127 = 8001 + 63 = 8064
f_model [8064] = 0; // assigns a zero to (63,63)
terrain [8064] = 75; // no elevation at (63,63)
return(1); // This line indicates to main() that the function worked.

// This function sets up the video mode, depending on system capabilities.

void setup_graphics() {
unsigned char colour_NO;
if (fg_testmode(25, 0)) { // tests for 640 x 480 resolution with 256 colours.
 old_vmode = fg_getmode();
else if (fg_testmode(18,0)) { // tests for 640 x 480 resolution with 16 colours if 256 colour mode not possible
 old_vmode = fg_getmode();
else {
 printf ("nThe Terrain Evolution Simulator requires VGA graphics capabilities");
 printf ("nto perform.");

//if (fg_getmode() == 25)
// {
 setrgb(255, 63, 63, 63); // colour 255 is white
 setrgb(254, 63, 0, 0); // colour 254 is red
// setrgb(253, 0, 63, 63); // colour 253 is cyan
 setrgb(253, 0, 53, 53); // colour 253 is cyan
 setrgb(252, 63, 63, 0); // colour 252 is yellow
 setrgb(251, 63, 0, 63); // colour 251 is purple
 setrgb(151, 0, 0, 0); // black in 236 colour mode
 setrgb(152, 0, 0, 63);
 setrgb(153, 63, 0, 0); // colour 153 is red
 setrgb(154, 0, 63, 0);
 setrgb(155, 20, 20, 20);

// }
// {
// setrgb(15, 0, 63, 63); // colour 15 is cyan
// setcolor(15);
// }
fg_box (0,200,0,40);

move_to(100, 20);
fg_print("Initializing video setup", 24);

//if (fg_getmode() == 25)
// {
 for (colour_NO = 1; colour_NO < 55; colour_NO++) // 50 shades of grey
 setrgb(colour_NO, (1.26*colour_NO), (1.26*colour_NO), (1.26*colour_NO));
 for (colour_NO = 50; colour_NO < 100; colour_NO++) // 50 shades of yellow-green
 setrgb(colour_NO, (0.63*(colour_NO-50)), (1.26*(colour_NO-50)), 0);
 for (; colour_NO < 150; colour_NO++) // 50 shades of green
 setrgb(colour_NO, 0, (1.26*(colour_NO-100)), (0.63*(colour_NO-100)));
// }
// {
// for (colour_NO = 1; colour_NO < 15; colour_NO++)
// setrgb(colour_NO, colour_NO*4, colour_NO*4, colour_NO*4);
// }

maxx = fg_getmaxx(); // should be 640
maxy = fg_getmaxy(); // should be 480
lines = fg_getlines(); // should be 30 text lines on screen

move_to(100, 20);
fg_box (0,200,0,40);
fg_print("Initializing video setup", 24);

fg_box(127, 511, 47, 431);


/* The following block of nested for loops generates the fractal terrain.
* This is accomplished through searching the f_model[] array for a value
* equal to one less than the count of the iteration. That is, on the first
* pass (where the iteration counter "iter8") is equal to one, the array is
* searched for positions with the value 0. In this manner the position (63,
* 63) is found during the fisrt iteration. In the following iterations -
* indicated with an "iter8" value of 2 through 6 - values in the f_model[]
* array of 1 through 5 are hunted. With the discovery of the positions
* containing the proper iteration value(s), eight surrounding points in the
* array are assigned an iteration value equal to iter8. This indicates
* that these points are of the next lesser heirarchy (31, 31), (31, 63), (31, 94), (94, 63),
* (94, 94), (63, 94), (31, 94), and (31, 63) are found from the current
* central point of (63, 63). In the second iteration, the eight points
* surrounding each of these first eight positions are assigned with the
* D_xy value of 16, assigned in LOOP LINE 4. Since a number of these points
* overlap, only 40 positions are determined in iteration two, instead of the
* 64 expected (from 8 first-iteration points * 8 second iteration points).
* This goes on until the fifth generation, at which point the scale value is
* one, and all positions in the array are determined. As each of the new
* positions are found, an elevation value is calculated for that position.
* This elevation value is derived by adding or subtracting a number randomly
* generated for the new position to the elevation value stored in the point
* of higher heirarchy from which the new point's coordinates were found.
* In this fashion, all elevation values across the entire terrain[] array are
* ultimately derived from the value of 0 (which is equal to a real elevation
* of 25 m, being 25000 mm above the zero elevation at -25000 in TES units)
* seeded at (63, 63). For each random number generation, the range of
* possible values is determined by the current iteration, as an argument
* passed to the vari8() function listed below. For the first iteration,
* the range of values is -22400 to 22400; for the second it is -11200 to
* 11200, and so on, until in the final iteration the vertical range is -700
* to 700. This halving of vertical range with every iteration is in keeping
* with the halving of the horizontal distance involved in the derivation of
* the new points' array positions. Lastly, it was realized that the three
* positions with a lesser Y value, as well the two positions with a lesser
* X value than the central position in each iteration following the first
* may have already been determined by a previous interpolation of the same
* iteration.
* This is possible due to the overlap of points based on the scaling system.
* To demonstrate, the three positions (31,47), (47,47), and (47,63) found in
* the second iteration share as a common third-iteration "offspring"
* position the point (39, 55). An elevation value for this point will be
* first assigned as the value of (31, 47) +/- a random value between -11200
* and 11200. It will then be overwritten when the program re-evaluates it
* as the value of (47, 47) +/- Û 11200, and again when the points around
* (47, 63) are determined. This problem gets worse in frequency with every
* iteration. To counteract this problem without creating as many as four
* more memory-swallowing arrays of 127 x 127 two-byte arrays, part of the
* code below checks first for a pre-existing value for each new position;
* it is for this reason that the terrain[] array was first set to zero for
* all points.
* If a non-zero value is found, it averages the elevation value it was going
* to assign (as found by the process described above) with the pre-existing
* value. This means that the value found from the (31, 47) position would
* be averaged with that found from (47, 47). This averaged value would then
* be averaged with that found from (63, 47), putting an unfortunate but
* inescapable weight on the last value.
* The procedure of averaging outlined above was the most realistic one found
* in creating a model by which each point could be traced in genesis to a
* nearby base point. */

void terrain_gen()
int rnd_value, // holder of randomly generated numbers
 D_xy=64, // the scale of X and Y change with every iteration, defined as 32 divided by 2 to the power of iter8
 dx, dy,
 iter8; // counter of iterations in generation of f_model[] array

unsigned int aplace;

randomize(); // this function initializes the random # generation routine for use.
//text_xy(253, maxx/2, maxy-30, "Generating fractal modelling framework");

for (iter8 = 1; iter8 < 7; iter8++) // LOOP LINE 1

 for (X = 0; X < 127; X++) // LOOP LINE 9
 for (Y = 0; Y < 127; Y++) // LOOP LINE 11
 if (f_model[tg_XY] == (iter8 - 1)) // LOOP LINE 13

/* These lines assign four of the eight f_model[] heirarchy values found
* in each iteration, for those positions diagonally
* surrounding the central points found with each iteration. */

/* The following lines assign the f_model[] values to points in the array
* which are of a distance set by D_xy and on the same horizontal or
* vertical. */

/* These lines create the elevation values at each of the eight positions
* for which f_model[] values were found above. */

 for (dx=-1;dx<2;dx++)
 for (dy=-1;dy<2;dy++)
 if ((dx || dy) && SANITY)
 f_model[aplace] = iter8;
 if (terrain[aplace])
 terrain [aplace] = (vari8(iter8,X,Y) + terrain[aplace])/2;
 terrain [aplace] = vari8(iter8,X,Y);

// if (f_model[tg_XY] == (iter8-1))
// draw(X, Y, (49 - (f_model[tg_XY]*8)));

 if (tiltz)
 terrain[aplace] += (int) ((Y - 63) * (tiltz/63.0));
 } // end of y for loop
 } // end of x for loop
 } // end of for iter8 loop.

/* These lines draw the legend of the f_model[] array to the screen beside the
* array. */

draw_rect(2, 118, 49, 178);
fg_box(0, 120, 47, 180);

fg_box(1, 119, 48, 179);

draw (-8, 7, 49);draw (-7, 7, 49);draw (-8, 8, 49);draw (-7, 8, 49);
 text_xy(151, 50, 73,"Iteration 1");
draw (-8, 12, 41);draw (-7, 12, 41);draw (-8, 13, 41);draw (-7, 13, 41);
 text_xy(151, 50, 88,"Iteration 2");
draw (-8, 17, 33);draw (-7, 17, 33);draw (-8, 18, 33);draw (-7, 18, 33);
 text_xy(151, 50, 103,"Iteration 3");
draw (-8, 22, 25);draw (-7, 22, 25);draw (-8, 23, 25);draw (-7, 23, 25);
 text_xy(151, 50, 118,"Iteration 4");
draw (-8, 27, 17);draw (-7, 27, 17);draw (-8, 28, 17);draw (-7, 28, 17);
 text_xy(151, 50, 133,"Iteration 5");
draw (-8, 32, 9); draw (-7, 32, 9); draw (-8, 33, 9); draw (-7, 33, 9);
 text_xy(151, 50, 148,"Iteration 6");

text_xy (151, 55, 170, "LEGEND");
text_xy (0, maxx/2, maxy-30, "Generating fractal modelling framework");
text_xy (253, maxx/2, maxy-30, "Fractal modelling framework complete");
text_xy (253, maxx/2, maxy-15, "press any key to continue");
noise = getche();
text_xy (0, maxx/2, maxy-15, "press any key to continue");
draw_rect(0, 120, 47, 180); */

text_xy (253, maxx/2, maxy-15, "Generating terrain model");

/* The following lines draw the terrain[] model to the screen, overwriting the
* previously drawn f_model[] array. */

for (X = 0; X < 127; X++)
 for (Y = 0; Y < 127; Y++)
 if (terrain[tg_XY] > 0)
// draw (X, Y, (int)(24.5 + (terrain[tg_XY]/1000.0)));
 draw (X, Y, (int)(50 + 3.5 * (terrain[tg_XY]/1000.0)));
 if (lakes[tg_XY])
 ldraw (X, Y, 152);
 draw (X, Y, 253); // Ocean...

//legend(0); // This call causes the legend to be drawn.

text_xy (0, maxx/2, maxy-30, "Fractal modelling framework complete");
text_xy (0, maxx/2, maxy-15, "Generating terrain model");
text_xy (253, maxx/2, maxy-30, "Terrain model generated");
text_xy (253, maxx/2, maxy-15, "Press any key to continue");
noise = getche();
text_xy (0, maxx/2, maxy-15, "Press any key to continue");
text_xy (253, maxx/2, maxy-15, "Smoothening terrain model");

/* This is the function - called in the terrain_gen() function above - which
* generates a new elevation value, which it then returns to the calling
* function. It takes as arguments the current scale factor (denoted in the
* parent function terrain_gen() as iter8), as well as the array position
* from which the currently active array position is derived; that is,
* (countX, countY) in terrain_gen(). The function begins by assigning the
* elevation value of the parent array position (midX, midY) to old_value.
* It then creates a new value randomly, defining the range through the
* "scale" variable, which is passed to the function as iter8() by
* terrain_gen(). If the random number generated was greater than half of
* the range, it (rnd_value) is added to old_value. If not, it is subtracted.
* The resulting value is then assigned to new_value, which is then
* returned to the calling function. */

// modified as of Nov 3, 1995 - I'm trying a new formula for the vertical
// difference with each pass. The basic formula is
// z(max)=44800/(1+(pow(0.5,(scale-1)))

int vari8(unsigned char scale,unsigned char midX,unsigned char midY)
int new_value, old_value, var_factor;
unsigned int rnd_value;

var_factor= (int) 10000 / (1+(pow(0.75, (scale -1))));

old_value = terrain[(midX*127) +midY]; // the elevation of the central calling position is copied to "old_value".
rnd_value = random(var_factor); // the random number (between zero and the value "var_factor") is assigned to rnd_value
new_value = old_value + ((var_factor/2) - rnd_value); // The new elevation value is assigned by adding a modified rnd_value to
 // old_value. Since the modified rnd_value may be below zero, the new value
 // may be below old_value.
return(new_value+500); // This line returns the new elevation value to the calling function.

/* The following code is designed to smoothen the terrain, to make its slopes
* more continuous, without jeopardizing the overall shape of the surface.
* This is accomplished through the use of a travelling window as outlined in
* the blurb above the values_gen() code. The window is used such that the
* current active position is set to the average of the four, six, or nine
* positions in the active window. This smoothing removes the unrealistic
* sharpness and obviously artificial terrain shapes which are a result of the
* terrain model generation process.

int smooth_4stats()
long sum; // the sum of all terrain elevation values for positions in the window.
unsigned int divisor; // used to derive an average of all window elevation values as a divisor of "sum". This is assigned a
 // value by determining the position of the active window in the river_a array.
char across, // the horizontal count across the active window.
 along, // the vertical count across the active window
 pass; // number of passes the smoothing algorithm makes
int s4_row,

int huge *tsmooth; // memory block used to hold terrain values during smoothening

tsmooth = (int huge*) farcalloc(16129, 2);
if (terrain == NULL)
 printf("error creating tsmooth array - insufficient memory!n");


/* This code determines the position of the active window within the river_a
* array by finding the position of the active central point. In this manner,
* a window in the corner is assigned a 2 x 2 dimension for the calucation
* of sum and divisor values, an edge window is assigned a shape of 3 x 2
* positions, and all other windows 3 x 3. */

for (pass = 0; pass < 2; pass++)
 for (X = 0; X < 127; X++)
 for (Y = 0; Y < 127; Y++)
 sum = 0;

 if (X == 0)
 { A = 0; B = 1; divisor = 2; }
 else if (X == 126)
 { A = -1; B = 0; divisor = 2; }
 else if ((X > 0) && (X < 126))
 { A = -1; B = 1; divisor = 3; }
 { A = 0; B = 0; divisor=1; }

 if (Y == 0)
 { C = 0; D = 1; divisor *= 2; }
 else if (Y == 126)
 { C = -1; D = 0; divisor *= 2; }
 else if ((Y > 0) && (Y < 126))
 { C = -1; D = 1; divisor *= 3; }
 { A = 0; B = 0; divisor=1; }

/* Using the parameters determined above, the sum of all elevation values
* within the active window is found, and then divided by the appropriate
* value to come up with a sum. This is then assigned to the entrain model,
* and added to the running total avecount for later division by the size of
* the array (16129 positions). */

 for (across = A; across <= B; across++)
 for (along = C; along <= D; along++)
 sum += terrain[((X+across)*127) + (Y+along)];
 tsmooth[s4_row + Y] = (int)(sum / divisor);
// tsmooth[s4_row+Y] = terrain[s4_row+Y];

/* These lines draw the smoothed terrain model[] to the screen, and record
* the lowest and highest elevation values in the array. */

 for (X = 0; X < 127; X++)
 for (Y = 0; Y < 127; Y++)
 terrain[s4_XY] = tsmooth[s4_XY];
 if (pass == 1)
 { // 24.5 is just a colour....
 if (terrain[s4_XY] > 0)
// draw (X, Y, (int)(24.5 + (terrain[s4_XY]/1000.0)));
 draw (X, Y, (int)(50 + 3.5 * (terrain[s4_XY]/1000.0)));
 if (lakes[s4_XY])
 ldraw (X,Y, 152);
 draw (X,Y, 253); // Ocean

 if (tsmooth[s4_XY] < lowcount)
 lowcount = tsmooth[s4_XY];
 else if (tsmooth[s4_XY] > highcount)
 highcount = tsmooth[s4_XY];
 avecount += tsmooth[s4_XY];
 } // end of pass loop


text_xy (0, maxx/2, maxy-30, "Terrain model generated");
text_xy (0, maxx/2, maxy-15, "Smoothening terrain model");
text_xy (253, maxx/2, maxy-30, "Terrain model smoothened");
text_xy (253, maxx/2, maxy-15, "press any key to continue");

// These lines display the highest, lowest, and average elevation values in the model.

hold_v = highcount/1000.0;
sprintf (output_h, "Highest elevation %.1f m", hold_v);
text_xy (253, maxx/2, 10, output_h);

hold_v = avecount / (16129000.0);
sprintf (output_a, "Average elevation %.1f m", hold_v);
text_xy (253, maxx/2, 22, output_a);

hold_v = lowcount/1000.0;
sprintf (output_l, "Lowest elevation %.1f m", hold_v);
text_xy (253, maxx/2, 35, output_l);

noise = getch();
text_xy (0, maxx/2, maxy-15, "press any key to continue");
text_xy (0, maxx/2, 10, output_h);
text_xy (0, maxx/2, 22, output_a);
text_xy (0, maxx/2, 35, output_l);

// These lines offer the choice to record the DEM as it currently is to a DEM.

if (!(ask))
 text_xy (253, maxx/2, maxy-15, "Do you wish to save this model to an Idrisi DEM? [y/n]");
 noise = getch();
 text_xy (0, maxx/2, maxy-15, "Do you wish to save this model to an Idrisi DEM? [y/n]");
 noise = toupper(noise);
 if (noise == 'Y')
text_xy (253, maxx/2, maxy-15, "Now generating aspect and slope values");

/* This function assigns slope and aspect values to each position in the
* terrain[] array. These are held in the delta_z[] and f_model[] arrays,
* respectively. These values are then used to determine the watershed which
* empties into each point in the array, which is equated linearly to the
* flow of water through that 10m x 10m position. At the same time, the
* amount of material which would be transported from each position - a
* function of the already derived slope and watershed values for that
* position. Note that f_model[] is reused here out of an unwillingness to
* pile up the variables.
int values_gen(unsigned char isit_first)
int new_value; // variable used to identify locally lowest elevation & highest local slope.
int X1, // temporary X position as watersheds are calculated
 Y1, // temporary Y position as watersheds are calculated
 DX, // horizontal distance across window
 DY; // vertical distance across window
double base, // holder of slope value for power function "pow()"
 exponent = 1.4; // holder of exponent of slope for erosional equation
int min_dz = 1000,
 max_dz = 0; // minimum and maximum slope value holders.
unsigned int vg_XY,

/* During the first pass of this function only, these lines initiate the
* river_a[], delta_z[], and entrain[] arrays. */

if (isit_first)
 river_a = (unsigned char huge*) farcalloc(16129, 2);
 if (river_a == NULL)
 printf("error creating river_a array - insufficient memory!n");
 noise = getch();

 delta_z = (int huge*) farcalloc(16129, 2);
 if (delta_z == NULL)
 printf("error creating delta_z array - insufficient memory!n");
 noise = getch();

 entrain = (int huge*) farcalloc(16129, 2);
 if (entrain == NULL)
 printf("error creating entrain array - insufficient memory!n");
 noise = getch();

for (X = 0; X < 127; X++)
 for (Y = 0; Y < 127; Y++)
 river_a[vg_XY] = 1; // initializes the points in river_a[] to one.
 delta_z[vg_XY] = 0; // initializes the points in delta_z[] to zero.
 entrain[vg_XY] = 0; // initializes the points in entrain[] to zero.

/* The following lines determine the slope at each point in the array toward
* the aspect of greatest down-hill direction. These are assigned to the
* delta_z[] array for all points. The aspect of these recorded slopes are
* kept in the array f_model[] */

 if (X == 0) { A = 0; B = 1; }
 else if (X == 126) { A = -1; B = 0; }
 else { A = -1; B = 1; }

 if (Y == 0) { C = 0; D = 1; }
 else if (Y == 126) { C = -1; D = 0; }
 else { C = -1; D = 1; }

 for (DX = A; DX <= B; DX++)
 for (DY = C; DY <= D; DY++)
 if (DX || DY)
 switch (DX*DY)
 case (-1): // ...or
 case (1): // We must fake a little trigonometry
 new_value = (int)((((terrain[vg_XY]) - terrain[aplace])/1.414213562) + 0.5);
 case (0): // Simple math....
 new_value = (terrain[vg_XY]) - terrain[aplace];
 if (new_value > delta_z[vg_XY])
 delta_z[vg_XY] = new_value;
 if ((DX == -1) && (DY == -1))
 f_model[vg_XY] = 1; // the aspect value for each position is
 else if ((DX == 0) && (DY == -1))
 f_model[vg_XY] = 2; // found with these lines, which determine
 else if ((DX == 1) && (DY == -1))
 f_model[vg_XY] = 3; // the direction based on the X and Y offset,
 else if ((DX == 1) && (DY == 0))
 f_model[vg_XY] = 4; // which may range from -1 to 1 each. The
 else if ((DX == 1) && (DY == 1))
 f_model[vg_XY] = 5; // directions assigned range from 1 through 8
 else if ((DX == 0) && (DY == 1))
 f_model[vg_XY] = 6; // starting in the upper left.
 else if ((DX == -1) && (DY == 1))
 f_model[vg_XY] = 7;
 else if ((DX == -1) && (DY == 0))
 f_model[vg_XY] = 8;
 } // end of for DY
// if (delta_z[vg_row+Y] == 0) // we're on a plain, so..
// f_model[vg_row+Y] = 0; // there is no aspect
 if (f_model[vg_XY] == 0)
 for (DX = A; DX <= B; DX++)
 for (DY = C; DY <= D; DY++)
 if ((DX || DY) && ((Y+DY)<127)&&((X+DX)<127)&&((X+DX)>=0)&&((Y+DY)>=0))
 if ((terrain[vg_XY] + river_a[vg_XY]) > terrain[aplace]
 && (terrain[vg_XY] <= terrain[aplace]))
 lakes[aplace]=terrain[vg_XY] + river_a[vg_XY];

/* The following lines record the minimum and maximum slopes found, a process
* which is only enacted in the first call of the function, since doing so
* each time would slow the evolution process. */

 if (isit_first)
 hold_v += delta_z[vg_XY];
 if (delta_z[vg_row+Y] > max_dz)
 max_dz = delta_z[vg_XY];
 else if (delta_z[vg_XY] < min_dz)
 min_dz = delta_z[vg_XY];

/* These torturous lines add the ponding effect. This has a look at the
* entire terrain, and finds local lows. It then checks a ring of positions
* around the local low, and determines whether those spots are above the
* low in elevation, but below the elevation of the water filling the low.
* This is how pools form. */

for (X=0;X<127;X++)
 for (Y=0;Y<127;Y++)
 if (lakes[vg_XY])
 A=0; B=0; C=0; D=0;
 while (lakec)
 toggle=0; // Has at least one value changed?
 if ((X+A)>0)
 if ((X+B)<126)
 if ((Y+C)>0)
 if ((Y+D)<126)
 if (!toggle) break;

 for (DX=A; DX<=B; DX++)
 for (DY=C; DY<=D;DY++)
 if ((DX==A || DX==B) || (DY==C || DY==D))
 if (terrain[aplace] < lakes[vg_XY]
 && terrain[aplace] >= terrain[vg_XY]
 && lakes[aplace] != lakes[vg_XY])
 for (tdx=-1; tdx<=1;tdx++)
 for (tdy=-1; tdy<=1; tdy++)
 if (lakes[(X+DX+tdx)*127+(Y+DY+tdy)] == lakes[vg_XY])
 lakes[aplace] = lakes[vg_XY];

// These lines write the minimum, maximum, and mean slope values in the delta_z[] array to the screen.

if (isit_first)
 base = tan((double)(max_dz/10000.0)) * 57.29577952;
 sprintf(output_h, "Highest slope %.1f¯", (float)base);
 text_xy(253, maxx/2, 10, output_h);

 hold_v /= 16129;
 base = tan((double)(hold_v/10000.0)) * 57.29577952;
 sprintf(output_a, "Average slope %.1f¯", (float)base);
 text_xy(253, maxx/2, 22, output_a);

 base = tan((double)(min_dz/10000.0)) * 57.29577952;
 sprintf(output_l, "Lowest slope %.1f¯", (float)base);
 text_xy(253, maxx/2, 35, output_l);

 text_xy (0, maxx/2, maxy-30, "Terrain model smoothened");
 text_xy (0, maxx/2, maxy-15, "Now generating aspect and slope values");
 text_xy (253, maxx/2, maxy-30, "Slope and aspect model generated");
 text_xy (253, maxx/2, maxy-15, "press any key to continue");
 noise = getch();

 text_xy (0, maxx/2, maxy-30, "Slope and aspect model generated");
 text_xy (0, maxx/2, maxy-15, "press any key to continue");
 text_xy (253, maxx/2, maxy-30, "Now generating watersheds & sediment values");
// text_xy (253, maxx/2, maxy-15, "This takes time - the dot traces progress");

/* This code determines the watershed values at each point by searching
* through the entire array for non-zero slopes. For each such value found,
* a subroutine follows the path downhill until a zero slope value is found.
* With every position found, the watershed value is incremented by one, to
* indicate that the first non-zero slope value position (at which the
* subroutine began) is contained within that position's watershed. This is
* accomplished through the determination of DX and DY from the aspect
* direction given in f_model[] for each position. These values, in turn,
* are added to the active X and Y coordinates so that the new, downhill
* (X,Y) are found, and then checked for slope value. In the case of
* non-zero slope, the value in f_model[] for that position is incremented by
* one, and the next downslope position is sought. For zero slope positions
* (local lows), no downslope position is sought.*/

for (X = 0; X < 127; X++)
 for (Y = 0; Y < 127; Y++)
 X1 = X;
 Y1 = Y;
 if (terrain[X1*127+Y1] > 0)
 do {
 switch ((f_model[vg_row + Y1] % 10))
 case 1: DX = -1; DY = -1; break; // These lines recreate the X and Y offset values used in the
 case 2: DX = 0; DY = -1; break; // previous function for use below.
 case 3: DX = 1; DY = -1; break;
 case 4: DX = 1; DY = 0; break;
 case 5: DX = 1; DY = 1; break;
 case 6: DX = 0; DY = 1; break;
 case 7: DX = -1; DY = 1; break;
 case 8: DX = -1; DY = 0; break;
 case 0: DX = 0; DY = 0; break;

// The following line adds one to the watershed value stored in river_a[] to the position downhill of the active position.

 if (terrain[((X1+DX)*127)+(Y1+DY)] > 0)

/* The following lines calculate the erosion due to wash and rilling which take
* place at each position. If several positions drain to the same position,
* that position is passed through by the algorithm several times, and the
* wash/rilling effect would be calculated each time, if the position were
* not flagged somehow after the first calculation. The flag used is the
* addition of ten to the f_model[] array for that position. While this has
* no effect on the aspect values held in that array, it allows for the
* determination of a previous calculation of that position's wash/rill
* erosion effects. The equation used is Î = (tan1.4‡ * L0.6)/10, where
* ‡ is slope angle, and L is ground length of slope. */

 if (f_model[(X1*127)+Y1] < 10)
 base = (double) (delta_z[vg_row+Y1]/10000.0);
 new_value = (int)(0.5 + (25.11886431 * pow(base, exponent)));
 entrain[vg_row+Y1] += new_value;
 entrain[vg_row+Y1+DY] -= new_value;
 f_model[vg_row+Y1] += 10;
/* if (isit_first)
 if (terrain[vg_row+Y] > 0)
 //draw(X1,Y1,(int)(24.5 + (terrain[vg_row+Y1]/1000.0)));
 draw (X, Y, (int)(50 + 3.5 * (terrain[vg_XY]/1000.0)));
 if (lakes[vg_row+Y1])
 draw(X1,Y1,253); // Ocean

 X1 += DX;
 Y1 += DY;
 } while ((f_model[vg_row+Y1]) && (X1>=0) && (Y1>=0) && (X1<=126) && (Y1<=126) && (delta_z[vg_row+Y1]));

/* At the end of every running of the values_gen() function, the following
* code draws the new terrain[] model to the screen, including the streams
* which might form upon the surface. No streams are assumed to form below
* a watershed area of 1000m˝, which yields a flow of 400 cubic metres per
* year if annual rainfall is assumed to approximate that of southern Ontario
* (800mm/yr), and that half of all precipitation is lost to evaporation 

leave a comment

By submitting this form you agree to the privacy terms.


Hi Michael!

Got your email re: my blogger site—I have two—to shy to let you see the personal one just yet but here is the one where my family posts messages for my son who is in Sri Lanka www.dusterbuster.blogspot.com

Saw your list of greatest movies. Being in the film business, I would have posted it differently (not based on box office!) and it definitely would have included "Like Water for Chocolate".

On my way to Australia to get me a husband! Yes—the immigration papers finally came through!! He can officially become a landed immigrant. I am flying through Auckland this time—will miss Sydney all together which is too bad—really wanted to see the real impact of those fires.

Watch CBC Newsword on the 22nd at 7:00 PM! Rough Cuts is airing a documentary that I worked on—"Shipyard's Lament".

And give me your honest opinion!!

Iris Merritt
2002.01.16 00:00:00

Hiya, Iris. Missed your documentary, but I hope the husband worked out! ;)


Well, it all started when I was looking for advice on woodworking tools. Somehow I ended up at your site and got sucked in by all the interesting stuff. For some reason I read you're whole autobiography and pretty much spent the last 2 hours reading about YOU. So weird as it may sound, it feels like I kind of know you in a never met you kind of way.

I live in San Diego and never really ventured farther north than San Francisco or West Virginia. The idea that people live in the frozen tundra of Canada just blows my mind. I just like the warm weather I guess.

By the way, I'm 24 yrs old; so not too much younger than you. And yes, I do have a real life; just procrastinating studying or getting a good nights sleep is all. I have finals for school tomorrow. I go to UCSD. So, just thought I should say something since you have provided me with so much entertainment. Take care and good luck dealing with the jack-hammers!

2003.06.09 00:00:00

Yes, the tundra really is that unimaginable. But as long as you steer clear of the bears, wolves, and SARS, you're okay.

Good luck with the hunt for woodworking tools!


I dreamt a murder..............

I had this strange dream last night, it was almost real. In my dream I was at a person's birthday party...The location and atmosphere was vivid; the house was modern with fancy ceilings, and walls with portraits hung up. A fireplace made of marble with a glass door, large windows with white curtins and a wide staircase.....

Music played in the background but, was unidentified! In the large family-room crowds of young men and women talked amongs each other with beers and wine in one hand. I remember seeing two family dogs...particularilly pitbulls, they didn't bark at my presence. I thought it was strange that it wasn't

2003.09.10 00:00:00

Yes? Yes? Oh, don't leave us hanging!


Someone Raping Me

Last night I had this dream, where I went to this grocery store with a lot of my girl friends. We were just cruising the isles when I realized that I had seen my old next door neighboor with his girlfriend. I stopped to say hi to him and he returned the greeting. We got to talking and he told me about this really cool party. We all thought it sounded like a good idea so we went to the party and had a few drinks.

But then everything got really fuzzy and the next thing I know, was that I was naked next to a guy I had never seen before in my entire life. And that day I woke up was my birthday.

I left the house immediately without calling anyone and the cops were out in the apartment hall. They grabbed my shoulder and took me to a hospital where they checked me for HIV.

I tested positive and started cry. I never wanted to tell my parents, so I went to my party and was trying to have a good time. When all of my friends were about to sing happy birthday to me, they stood me up on the table, and started singing. Then the same guy who had raped me came in and I started crying.

My parents made me tell them what had happened and it seemed like they didn't even care about me. So I was just left alone and no one ever wanted to help me. No one would even talk to me about it.

Katherine Bynum
2002.11.14 00:00:00

Ugh. I'm going to take your word for it that this was really a dream? Or - even better - a prank?



I had this dream a while ago and the next night it continued. Rubble of houses and nature. I curled up in fright of the scene. War planes zoomed above me ,the skys dark grey. A boy, I knew him from my school. He guided me through the rubble to a hill by the state capitol. Half the valley was in flames and crators. The moon in shards above.(end of the night and I woke up, the next night it continued)I somehow got to a worknig TV, The whole US was in ruins. We were to evacuate immideatly before the enemy gets to us, but, who was our enemy? I awoke soon after.....is this just a dream to you?

2003.06.30 00:00:00

Phrew! I don't know what to say. Sounds a bit like a politically-inspired episode of one of those anime flicks.


In someone elses body....

Last night I dreamt that I was a girl.....dating this freak, some over protective loser.....

Yeah, pretty messed up huh? I mean, I did'nt do anything like that....Hell no, it was really wierd. It was like I was living someone elses life, I did'nt recognize anything that related to things that exist in my life........

The house that I was in was large, a rich home. Nothing like my house! Two dogs? I don't have pets!

Plus It was my birthday, people that I did'nt know were there, and what made it strange was that I was a girl who ahd broken up with some guy....

He came by to the party and asked me to go with him to the liqour store. He drove passed a isolated road at 90 miles per hour, and was menacing me with his fist.

I was emmotionally distrought, in conflict with him shouting "Stop your gonna kill us!" I guess he was jealous cuss he slapped my face.

I was in panic and horrified....I had never felt soo vunerable in a dream before. Soo fragile and powerless.....like I was'nt a man. Shit, it was messed up! It just was'nt me!

Anyways he tried to drive into a lake, atempting to kill me and himself. I turned the steering wheel rapidly to avoid disaster but,instead he pushed me towards the door, decided to change directions. He drove straight into a conctrete fence in someones back yard.

The man was killed on impact, and I was critically injured yet I still managed to pull him out of the car corpse and all. Police, investigaters, and ambulances surrounded the scene asking quetions......everything else was blurry....but I remember taking a bubble bath crying and all fucked up and emmotional.

2003.09.11 00:00:00

Bizarre. I guess the moral of the story is that a bubble bath will help even dreamtime ersatz women cope in a crisis.... ;)


Even Stranger A Dream

Last night I had the weirdest dream... And it's making real life today very unsettling... I had dreamt that my mother took very ill and was shrouded in a blanket in a loveseat, coughing, upon which she died. Seeing her corpse its was yellow and shriveled. My dad was situated near my dead mother and held her hand silently weeping even after death without release. Funeral... morning... next scene... We(father, sister, and I) were returned to my house, and at somepoint in a small isolated room. There was one door that had shut tight, and the room was filling with water, until it was filled. My dad and sister survived. My corpse was the palest of whites and still damp when I beheld it. Even though I was dead I still walked with the living and my dead mother was their with me. And slowly I made myself known to them, I spoke to my father, my sister( but befriefly), and various other family members. I was present in church and at the house of my girlfriend where I still expressed my undying love. So for the remainder more and more people accepted I was dead but still spoke of me as directly to me because at certain times they would feel my presence and be able to communicate, and at other times I was not acknowledge. But my girlfriend always spoke with me, even across the fone. We still continued the relationship after my death, but I had no hope of ever being revived and felt accordingly... What does this macabre dream imply?? Any insites would be appreciated

Dean Gunaj
2003.11.23 00:00:00

Heh! I have no insights. Sounds like a difficult dream, for sure.


the basement

I had a very strange dream last night.

Although I am not so sure it was a dream, I think I astral projected.

I was in my bed, and for some reason, I flew into the living room. I was suspended in air. I seen the door to the basement open up all by itself.

I floated over to the door and looked down the stairs. It was dark, but suddenly, the vacuum cleaner that doesn't work started up and I could hear it. It was moving on it's own and when it got to the bottom step, it stopped. Even in my dream, I was scared.

I slammed the door and locked it, and I remember leaning up against the door and hoping nothing would come out.

I woke up still afraid that something was in my basement.

2004.01.24 00:00:00



bizzare dream

I have dreams from time to time that leave me physically exhausted when i get up in the morning. By that i mean i feel like ive been in the gym for a few hours when all ive been doing is sleeping. Its so strange. It started with a dream that I was standing next to yuri gargarin? (first man in space) at the launch pad as he was about to go into space, the dream was in colour and in the dream i felt the g-forces that yuri felt as the rocket launched off the pad. I felt the weightlessness of being in space and when i woke up i was so tired and my body felt so sore that i could hardly walk. weird eh? p.s. i also have dreams where i can fast forward or rewind the dream like a vcr. is that common?

2004.07.06 00:00:00

Yuri Gregarin, cool!

I wish I could fast-forward or rewind, some times.


weird dream

I dreamt last night that i was sitting on a bridge, and then i went underneath the bridge and there was a pile of dirt. I was pushed into the dirt and held there, i was trying to move the dirt around with my hands to get free. It didnt work, i had gotten some of the dirt in my mouth during it all and when i woke this morning my hands had dirt on them and i could taste and rub some dirt off my tongue. (now i know it wasnt a joke by anyone my door was loked from the inside and my room is on the second floor.) what is this type of dream called and why does it happen?

2004.07.09 00:00:00

Called? Uh, I think it's called fncked up!


Bizarre dreams

Have bizarre dreams all the time and whats worse is that I remember them. Contantly have dream where I am dead but can come back to life—many different ways—usually being reconstitued with water (yea—weird I know). I think that has to do with needing to get 'living water' (the Bible—church) back in my life. The one that disturbs me the most is when I posses a dead person's body—that freaks me out—have no explaination execpt that maybe my own life is too boring? Third and last weird dream I had today—gave birth to very premie baby—isn't having sex necessary for that?!? Comments welcome, sorry, offers to make babies not accepted here.

Too Normal Looking
2004.07.15 00:00:00



Hey - I was just checking out your photography. You have a real talent there. Some of those pictures are very interesting. (Cute kids too). I just re-tooled my music studio. It's like going from Frankensteins's lab to a alien base on the moon. It's friggen' cosmic!

Kevin Atwood
2010.02.17 13:53:11

Thanks for that, Kevin! Good to hear that you're still working in (and on) the home studio.

We've just launched our business, by the way (see link in upper-right).


Just popping in to say hi, and to have a bit of a look around (looks like I could be looking a while).

2011.07.29 23:01:13



Hi Michael. It's been a while..... I hope you are doing great and you are happy in Japan. Good luck , Margaret

2011.11.07 05:09:18

Hello, Margaret! Thanks for writing.

I\'m back in Canada as of this January. We came back after two grueling years of the economic downturn in that country. Happily, it was about two months before the earthquake, tsunami, and the incident at the nuclear power plant.

How\'s everything on the west coast? I see the B&B\'s still on the go. 8)


wow. I'll bet he will remember it, too.

2012.08.19 21:03:52

This morning he was talking about a time that you and he and I and his mum went for a walk in a tunnel in a forest. After asking him a few more questions I think we're pretty sure that he was talking about the bamboo forest in Kyoto. When he was about nine months old. Does that make any sense at all?


this is pretty after the fact but it just came to my attention that there was a hello kitty hell website. i started reading comments and saw yours wondering if the canadian really made the hello kitty combat patch on his fatigues. i can assure you he did because i am his sister and i heard about it and saw pics of it back then!! just the irreverent family humour coming thru! there are only 2 patches and he was in afghanistan at the time. i was amused to see all sorts of kitty sacriliges on the website!! long live a warped sense of humour!! :) Chuckling Canuck

kat (not kitty!!)
2012.11.23 20:06:46

Good to know! Thanks for taking the time to write.

Is your brother back in Canada?


Michael, Thanks much for the shitlist! Spammers are the devil's spawn.

2013.10.22 09:46:02

Can't say it's been a pleasure to build, but I'm glad you found it useful James.


I have some question relating to jewelry photography and I was wondering if you might be kind enough to provide some insight. The article you wrote was great but there are still some question left unanswered Thank you

2014.12.31 15:00:32

Happy new year, Tony. I'm not sure that there's a lot more I can add about photographing jewelry, but what would you like to know?


Michael -

How does one get off your "shitlist"? The IP address of our car club's website and listserver (zmgna.org at xxx.xxx.xxx.xxx) appears on your shitlist. I don't know how or why it got there, but we are having random problems with our members not being able to get messages, and as far as I can determine, your list is the only SPAM list that includes our IP.

Chris Kotting
2015.06.29 00:00:00

Hi! You've just been removed from my list, but I know that others have picked mine up over the years and may still be doing so. Moreover, if you got on my list by spam being sent to my little domain, my guess is the IP was used for sending a spam to others. There are a wide number of blacklists out there.



That said, I've had a look at the following site, your IP came up clean.


No response from this one, but the lists look like the real deal, you might check each individually.


P.S. You're the first person to ever write to have an IP removed.


Hi, My husband is a Permanent Resident in Canada and were both filipinos. He sponsored me and submitted the application in CIC Mississauga. Is that what you mean inland? I:m currently in the Philippines waiting for the email of CIC since my husband submitted it last Sept 30, 2015. How long it would take to wait for there update regatding this?regarding

2015.11.24 15:55:34

It's been almost five years, but I believe that if you filed through Mississauga, you're in the outland process. The CIC website has good figures on the wait times by country.


As of today, that's 17 months, but that's after the "64 day" first setp (see the link near the top of the page).

You did the right thing by choosing the outland process. The inland process has a total of 27 months. And if your application is denied, you're finished!


All the best with your application Angelie.


Hi Michael. I'm from the Philippines and married to a Canadian Citizen. My husband have just submitted our spousal visa application. My concern is that I used my maiden name in the application. I plan to submit my updated Surname once I get my updated passport with my husband's surname. Do you think that they will consider the change during the application? If yes, when should I submit my updated passport? (Should I send it while the application is still in CPC or when it's in the visa office in country?) How can I submit the change of my surname? Will it delay the processing time?

2016.03.24 12:34:07


Thanks for writing. I don't know if it will delay your processing time. Whatever you do, make sure you tell the CPC about changes in your status as soon as possible. Surprising them with something like a name-change could be a show-stopper altogether: my (amateur) advice is not to risk it.

If you've already been told that your case has been sent on to the visa office in Manila, then I suggest you contact that office. I always found the in-country office more helpful than the general CPC help line in Canada. Try to find a website for the office in Manila and see if that offers any help.

Best of luck with your application. We've been in Canada for five years, now, we've had a daughter here and life is stable. It's worth the hassle!


Hi Michael, I am a Canadian but has been working in ASIA for the past 20 years. My son who is 13 years old is currently going to school in Canada, where my wife, a Japanese, is taking care my son in Canada on a visitor status. I applied the Outland spousal sponsorship last Oct and we got our "decision made" last week. While we are waiting for the COPR, I would like to ask do I, as the sponsor, need to return to Canada right away with my wife after she gets her PR? I have a job here in Asia which takes time for me to resign and I am not sure if they will start to deem me as a resident (from a tax perspective) right after my wife becomes a PR. Can I return to Canada on a later date and would it jeopardize my wife PR status?

2017.03.03 17:35:21

Hello, KK. I'd love to respond but you didn't provide your email address.

I am not an immigration specialist or a lawyer, but here goes:

You shouldn't need to accompany her, but it would be a good idea if your son were there: kids have a positive effect on these interactions.

Also, you only jeopardize the PR status if you don't show that you're living together. You don't have to be in Canada, but you do have to be together wherever you live, for the majority of each period they consider. The period changed recently, and will recently change again, so I won't quote the current statutes, you should look that up. This might be a good spot.



Hi Michael !

Rohito Bhambhani
2019.02.26 16:56:19

Why hello, Rohito. How nice to hear from you after all this time. How's the wife and kids?


Dear Michael: I was wondering if you could send me your old PR sponsorship story..was about six pages printed out, if I recall correctly. Perhaps you took it off your site as some of the info is out of date? I would still like to review it. Canadian, with a Japanese spouse, in Japan. We are legally divorced, as that was the only way for her to survive there. The Canadain Consulate, in Fukuoka, now defunct, lost our marriage registration, among other things. A Canadian woman I used to know, daugther of my parent's friends, is going through hell to get her British husband into Canada. Both had UN jobs in Europe, and the type to folow the rules. They sent him on a plane back to the UK one Xmas. Thanks for the very useful website. Regards, Timothy

Timothy Breitkreuz
2019.07.09 16:49:13

Hi, Timothy;

Thanks for reaching out. Yes, I took all that off the site when I rebuilt on the new domain because it was already eight years old. I'll see what I can dig up and will send that along.


rand()m quote

A life spent making mistakes is not only more honorable but more useful than a life spent doing nothing.

—George Bernard Shaw