Index: misc/m.cogo/m.cogo.html
===================================================================
--- misc/m.cogo/m.cogo.html	(revision 36733)
+++ misc/m.cogo/m.cogo.html	(working copy)
@@ -20,6 +20,12 @@
    P24 S 04:18:56 E 230
    ...
 </pre></div>
+Alternately, with the <b>-d</b> flag:
+<div class="code"><pre>
+  P23 336.763333 340
+  P24 175.684444 230
+  ...
+</pre></div>  
 
 <P>
 The first column may contain a label and you must use the <B>-l</B>
@@ -41,9 +47,18 @@
    ...
 </pre></div>
 <P>
-Unless specified with the <B>coord</B> option, calculations begin from (0,0).
+Slope correction is also possible. The input format with the <b>-z</b> flag set
+is <EM>&lt;label&gt; &lt;bearing&gt; &lt;slope angle&gt; &lt;distance&gt;</EM>. 
+The outputs in this case are xyz coordinates.
+Note that that slope angle is always expressed in decimal degrees. If slope angles 
+are included in the input, the distance is assumed to be slope distance.
 
 <P>
+Unless specified with the <B>coord</B> option, calculations begin from (0,0,0).
+If the input does not include slope angles, the z-value of the <B>coord</B> option 
+is ignored.
+
+<P>
 For those unfamiliar with the notation for bearings: Picture yourself in the
 center of a circle.  The first hemispere notation tell you whether you should
 face north or south.  Then you read the angle and either turn that many
Index: misc/m.cogo/main.c
===================================================================
--- misc/m.cogo/main.c	(revision 36733)
+++ misc/m.cogo/main.c	(working copy)
@@ -1,12 +1,16 @@
 /* ====================================================================
  * PROGRAM: m.cogo
  * AUTHOR:  Eric G. Miller <egm2@jps.net>
- * DATE:    September 29, 2001
+ *          David Mahoney <mahoneyd@unbc.ca>
+ * DATE:    April 15, 2009
  * PURPOSE: Translates simple COordinate GeOmetry with a format of
- *          "[label] <bearing> <distance>" to "X Y [label]".
+ *          "[label] <bearing> [slope] <distance>" to "X Y [Z] [label]".
  *          
  *          Example:  "P0001 S 88-44-56 W 6.7195"
  *                    "-6.7178980970 -0.1467153972 P0001"
+ *          
+ *          Bearings in decimal degrees are also supported, as are 
+ *          slope corrections.
  *                   
  *          The input formats are very limited.  
  * --------------------------------------------------------------------
@@ -23,6 +27,7 @@
 #include <string.h>
 #include <grass/gis.h>
 #include <grass/glocale.h>
+#include <grass/config.h>
 
 #define DEG2RAD(a) ((a) * M_PI / 180.0)
 #define RAD2DEG(a) ((a) * 180.0 / M_PI)
@@ -30,12 +35,28 @@
 #define FORMAT_1 " %s %1[NS] %d%c%d%c%lf %1[EW] %lf "
 #define FORMAT_2 " %1[NS] %d%c%d%c%lf %1[EW] %lf "
 #define FORMAT_3 " %lf %lf %s "
+#define FORMAT_4 " %s %lf %lf "
+#define FORMAT_5 " %lf %lf "
+#define FORMAT_1Z " %s %1[NS] %d%c%d%c%lf %1[EW] %lf %lf "
+#define FORMAT_2Z " %1[NS] %d%c%d%c%lf %1[EW] %lf %lf "
+#define FORMAT_3Z " %lf %lf %lf %s "
+#define FORMAT_4Z " %s %lf %lf %lf "
+#define FORMAT_5Z " %lf %lf %lf "
 
+#define HASLABEL 1
+#define DD 2
+#define HASLABEL_DD 3
+#define Z 4
+#define HASLABEL_Z 5
+#define DD_Z 6
+#define HASLABEL_DD_Z 7
 
 struct survey_record
 {
     char label[20];
     int haslabel;
+    int use_dd;
+    int hasz;
     char n_s[2];
     char e_w[2];
     int deg;
@@ -44,6 +65,9 @@
     double dist;
     double rads;
     double dd;
+    double azimuth;
+    double z;
+    double slope;
     double x;
     double y;
 };
@@ -51,23 +75,60 @@
 
 static void print_coordinates(FILE * outfile, struct survey_record *in)
 {
-    if (in->haslabel == YES)
-	fprintf(outfile, "%f %f %s\n", in->x, in->y, in->label);
+    if (in->haslabel == YES) {
+    if (in->hasz == YES)
+        fprintf(outfile, "%f %f %f %s\n", in->x, in->y, in->z, in->label);
     else
-	fprintf(outfile, "%f %f\n", in->x, in->y);
+	    fprintf(outfile, "%f %f %s\n", in->x, in->y, in->label);
+    }
+    else {
+    if (in->hasz == YES)
+        fprintf(outfile, "%f %f %f\n", in->x, in->y, in->z);
+    else
+	    fprintf(outfile, "%f %f\n", in->x, in->y);
+    }
+    return;
 }
 
 
 static void print_cogo(FILE * outfile, struct survey_record *in)
 {
-    if (in->haslabel == YES)
-	fprintf(outfile, "%s %s %d:%d:%.3f %s %f\n",
-		in->label, in->n_s, in->deg, in->min, in->sec,
-		in->e_w, in->dist);
-    else
-	fprintf(outfile, "%s %d:%d:%.3f %s %f\n",
-		in->n_s, in->deg, in->min, in->sec, in->e_w, in->dist);
-
+    switch ( in->haslabel | (in->use_dd << 1) | (in->hasz << 2)) {
+        case HASLABEL:
+        fprintf(outfile, "%s %s %d:%d:%.3f %s %f\n",
+            in->label, in->n_s, in->deg, in->min, in->sec,
+            in->e_w, in->dist);
+        break;
+        case DD:
+        fprintf(outfile, "%.6f %.6f\n",
+            in->azimuth, in->dist);
+        break;
+        case HASLABEL_DD:
+        fprintf(outfile, "%s %.6f %f\n",
+            in->label, in->azimuth, in->dist);
+        break;
+        case Z:
+        fprintf(outfile, "%s %d:%d:%0.3f %s %f\n",
+            in->n_s, in->deg, in->min, in->sec, in->e_w, in->slope, in->dist);
+        break;
+        case HASLABEL_Z:
+        fprintf(outfile, "%s %s %d:%d:%0.3f %s %.3f %f\n",
+            in->label, in->n_s, in->deg, in->min, in->sec, in->e_w, 
+            in->slope, in->dist);
+        break;
+        case DD_Z:
+        fprintf(outfile, "%.6f %.6f %f\n",
+            in->azimuth, in->slope, in->dist);
+        break;
+        case HASLABEL_DD_Z:
+        fprintf(outfile, "%s %.6f %.6f %f\n", in->label, in->azimuth,
+            in->slope, in->dist);
+        break;
+        default:
+        fprintf(outfile, "%s %d:%d:%.3f %s %f\n",
+            in->n_s, in->deg, in->min, in->sec, in->e_w, in->dist);
+    }
+    return;
 }
 
 
@@ -83,26 +144,76 @@
 }
 
 
+double az2dd(double azimuth) {
+    return fmod((450 - azimuth),360.0);
+}
+
+double dd2az(double dd) {
+    return az2dd(dd);
+}
+
+
 static int parse_forward(const char *in, struct survey_record *out)
 {
     char dummy1;
     int status;
+    
+    /* Parse a the input line, using one of eight different formats
+     * The format to use depends on whether or not there are labels,
+     * slopes, and whether or not the bearing is in decimal degrees. */
 
-    if (out->haslabel == YES) {
-	status =
-	    sscanf(in, FORMAT_1, out->label, out->n_s, &out->deg, &dummy1,
-		   &out->min, &dummy1, &out->sec, out->e_w, &out->dist);
+    switch ( out->haslabel | (out->use_dd << 1) | (out->hasz << 2)) {
+    case HASLABEL:
+    status = sscanf(in, FORMAT_1, out->label, out->n_s, &out->deg, 
+         &dummy1, &out->min, &dummy1, &out->sec, out->e_w, &out->dist);
+    if (status != 9)
+        return 0;
+    break;
+    case DD:
+    status = sscanf(in, FORMAT_5, &out->azimuth, &out->dist);
+    if (status != 2)
+        return 0;
+    break;
+    case HASLABEL_DD:
+    status = sscanf(in, FORMAT_4, out->label, &out->azimuth, &out->dist);
+    if (status != 3)
+        return 0;
+    break;
+    case Z:
+    status = sscanf(in, FORMAT_2Z, out->n_s, &out->deg, &dummy1,
+            &out->min, &dummy1, &out->sec, out->e_w, &out->slope, &out->dist);
+    if (status != 9)
+        return 0;
+    break;
+    case HASLABEL_Z:
+    status = sscanf(in, FORMAT_1Z, out->label, out->n_s, &out->deg, 
+         &dummy1, &out->min, &dummy1, &out->sec, out->e_w, 
+         &out->slope, &out->dist);
+    if (status != 10)     
+        return 0;
+    break;
+    case DD_Z:
+    status = sscanf(in, FORMAT_5Z, &out->azimuth, &out->slope, &out->dist);
+    if (status != 3)
+        return 0;
+    break;
+    case HASLABEL_DD_Z:
+    status = sscanf(in, FORMAT_4Z, out->label, &out->azimuth, 
+        &out->slope, &out->dist);
+    if (status != 4)
+        return 0;
+    break;
+    default:
+    status = sscanf(in, FORMAT_2, out->n_s, &out->deg, &dummy1,
+            &out->min, &dummy1, &out->sec, out->e_w, &out->dist);
+    if (status != 8)
+        return 0;
     }
-    else {
-	status = sscanf(in, FORMAT_2, out->n_s, &out->deg, &dummy1,
-			&out->min, &dummy1, &out->sec, out->e_w, &out->dist);
-    }
 
-    if ((status != 9 && out->haslabel == YES) ||
-	(status != 8 && out->haslabel == NO))
-	return 0;
+    /* Convert DMS bearings to decimal degrees */
+    if ( out->use_dd == NO ){
+    out->dd = DMS2DD(out->deg, out->min, out->sec);
 
-    out->dd = DMS2DD(out->deg, out->min, out->sec);
     if (out->n_s[0] == 'N') {
 	if (out->e_w[0] == 'E') {
 	    out->dd = 90.0 - out->dd;
@@ -128,6 +239,20 @@
     else {
 	return 0;
     }
+    }
+
+    /* convert an azimuth to decimal degrees. */
+    else {
+        out->dd = az2dd(out->azimuth);
+    }    
+
+    /* calculate elevation and horizontal distance */
+    if (out->hasz) {
+        out->dist = out->dist * cos(DEG2RAD(out->slope)); 
+        out->z += out->dist * tan(DEG2RAD(out->slope));
+    }    
+
+    /* calculate x and y coordinates */
     out->rads = DEG2RAD(out->dd);
     out->x += out->dist * cos(out->rads);
     out->y += out->dist * sin(out->rads);
@@ -138,27 +263,46 @@
 
 static int parse_reverse(const char *in, struct survey_record *out)
 {
-    double x, y;
+    double x, y, z;
     int status;
 
-    status = sscanf(in, FORMAT_3, &x, &y, out->label);
-    if (status < 2)
-	return 0;
-    else if (status == 2)
-	out->haslabel = NO;
-    else
-	out->haslabel = YES;
+    if (out->hasz == YES) {
+        status = sscanf(in, FORMAT_3Z, &x, &y, &z, out->label);
+        if (status < 3)
+        return 0;
+        else if (status == 3)
+        out->haslabel = NO;
+        else
+        out->haslabel = YES;
+    }    
+    else {
+        status = sscanf(in, FORMAT_3, &x, &y, out->label);
+        if (status < 2)
+	    return 0;
+        else if (status == 2)
+	    out->haslabel = NO;
+        else
+	    out->haslabel = YES;
+    }
 
-    G_debug(5, "IN:  x=%f  y=%f  out->x=%f  out->y=%f", x, y, out->x, out->y);
+    G_debug(5, "IN:  x=%f  y=%f z=%f out->x=%f  out->y=%f out->z=%f", x, y, z, out->x,
+            out->y, out->z);
 
     out->rads = atan2(y - out->y, x - out->x);
     out->dist = hypot(x - out->x, y - out->y);
+    out->slope = RAD2DEG(atan((z - out->z)/out->dist));
+    out->dist = hypot( out->dist, z - out->z);
     out->x = x;
     out->y = y;
+    out->z = z;
     out->dd = RAD2DEG(out->rads);
 
-    G_debug(5, "OUT: out->dd=%f  out->dist=%f", out->dd, out->dist);
+    G_debug(5, "OUT: out->dd=%f  out->dist=%f out->slope=%f", out->dd, out->dist,
+            out->slope);
 
+    out->azimuth = dd2az(out->dd);
+
+    /* convert decimal degrees to DMS */
     if (out->rads >= 0.0) {
 	out->n_s[0] = 'N';
 	out->n_s[1] = '\0';
@@ -210,6 +354,8 @@
     struct Flag *format;
     struct Flag *quiet;
     struct Flag *reverse;
+    struct Flag *azimuth;
+    struct Flag *three_d;
     struct GModule *module;
     FILE *infile, *outfile;
     struct survey_record record;
@@ -235,6 +381,14 @@
     quiet->key = 'q';
     quiet->description = _("Suppress warnings");
 
+    azimuth = G_define_flag();
+    azimuth->key = 'd';
+    azimuth->description = _("Bearings are in decimal degrees");
+
+    three_d = G_define_flag();
+    three_d->key = 'z';
+    three_d->description = _("Include slope calculations");
+
     reverse = G_define_flag();
     reverse->key = 'r';
     reverse->description =
@@ -250,11 +404,11 @@
 
     coords = G_define_option();
     coords->key = "coord";
-    coords->key_desc = "x,y";
+    coords->key_desc = "x,y,z";
     coords->type = TYPE_DOUBLE;
     coords->required = NO;
-    coords->description = _("Starting coordinate pair");
-    coords->answer = "0.0,0.0";
+    coords->description = _("Starting coordinate triplet of the form");
+    coords->answer = "0.0,0.0,0.0";
 
     if (G_parser(argc, argv) != 0)
 	exit(EXIT_FAILURE);
@@ -306,11 +460,27 @@
 	record.y = strtod(coords->answers[1], &ss);
 	if (ss == coords->answers[1])
 	    G_fatal_error(_("Converting starting coordinate pair"));
+    record.z = strtod(coords->answers[2], &ss);
+    if (ss == coords->answers[2])
+        G_fatal_error(_("Converting starting coordinate triplet"));
     }
     else {
-	record.x = record.y = 0.0;
+	record.x = record.y = record.z = 0.0;
     }
 
+    if (azimuth->answer) {
+        record.use_dd = YES;
+    }
+    else {
+        record.use_dd = NO;
+    }
+    if ( three_d->answer) {
+        record.hasz = YES;
+    }
+    else {
+        record.hasz = NO;
+    }    
+
     while ((cptr = next_line(infile))) {
 	linenum++;
 
