diff --git a/configure.in b/configure.in
index ce192f61..791651da 100644
--- a/configure.in
+++ b/configure.in
@@ -337,6 +337,7 @@ AC_OUTPUT( \
src/Prep/Photo/Makefile \
src/Prep/ShapeFile/Makefile \
src/Prep/TGVPF/Makefile \
+ src/Prep/UserDef/Makefile \
src/Utils/Makefile \
src/Utils/cdrom/Makefile \
src/Utils/download-map/Makefile \
diff --git a/src/Prep/Makefile.am b/src/Prep/Makefile.am
index 0a0ee2da..c6fa7377 100644
--- a/src/Prep/Makefile.am
+++ b/src/Prep/Makefile.am
@@ -7,4 +7,5 @@ SUBDIRS = \
MergerClipper \
Photo \
ShapeFile \
- TGVPF
+ TGVPF \
+ UserDef
diff --git a/src/Prep/UserDef/.cvsignore b/src/Prep/UserDef/.cvsignore
new file mode 100644
index 00000000..8c0055fd
--- /dev/null
+++ b/src/Prep/UserDef/.cvsignore
@@ -0,0 +1,4 @@
+.deps
+Makefile
+Makefile.in
+tguserdef
diff --git a/src/Prep/UserDef/Makefile.am b/src/Prep/UserDef/Makefile.am
new file mode 100644
index 00000000..49ac5715
--- /dev/null
+++ b/src/Prep/UserDef/Makefile.am
@@ -0,0 +1,11 @@
+bin_PROGRAMS = tguserdef
+
+tguserdef_SOURCES = tguserdef.cxx
+
+tguserdef_LDADD = \
+ $(top_builddir)/src/Lib/Geometry/libGeometry.a \
+ $(top_builddir)/src/Lib/Polygon/libPolygon.a \
+ $(top_builddir)/src/Lib/poly2tri/libpoly2tri.a \
+ -lsgbucket -lsgmisc -lsgmath -lsgio -lsgxml -lsgdebug -lgenpolyclip -lz
+
+INCLUDES += -I$(top_srcdir)/src/Lib
diff --git a/src/Prep/UserDef/sample.xml b/src/Prep/UserDef/sample.xml
new file mode 100644
index 00000000..a2e01064
--- /dev/null
+++ b/src/Prep/UserDef/sample.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+ North Gower
+ Town
+ -75.72 45.13
+ 1000
+
+
+
+ Carp
+ Town
+ -76.04 45.35
+ 500
+
+
+
+ Kinburn
+ Town
+ -76.20 45.38
+ 500
+
+
+
+ Galetta
+ Town
+ -76.27 45.42
+ 500
+
+
+
+
+
+ Ottawa-Carleton Road 6
+ Road
+ 10
+
+ -75.72 45.13
+ -75.77 45.11
+ -75.90 45.03
+
+
+
+
+
+
+ Kanata (north end)
+
+ false
+
+ -75.90 45.34
+ -75.91 45.34
+ -75.92 45.33
+ -75.91 45.32
+ -75.90 45.33
+ -75.91 45.33
+
+
+
+
+
diff --git a/src/Prep/UserDef/tguserdef.cxx b/src/Prep/UserDef/tguserdef.cxx
new file mode 100644
index 00000000..9ec7d23d
--- /dev/null
+++ b/src/Prep/UserDef/tguserdef.cxx
@@ -0,0 +1,215 @@
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+SG_USING_STD(vector);
+
+static string prog_name;
+static string work_dir = ".";
+static Rectangle bounds(Point3D(-180, -90, 0), Point3D(180, 90, 0));
+static FGPolygon bounds_poly;
+
+
+/**
+ * Parse two vertices from a string.
+ */
+static const char *
+parse_point (const char * s, Point3D &p)
+{
+ char * endptr;
+ float x = strtof(s, &endptr);
+ if (endptr == s)
+ return 0;
+ else
+ s = endptr;
+ float y = strtof(s, &endptr);
+ if (endptr == s) {
+ SG_LOG(SG_TERRAIN, SG_WARN, "Uneven number of vertices!!");
+ return 0;
+ }
+ p.setx(x);
+ p.sety(y);
+ return endptr;
+}
+
+static void
+add_point (SGPropertyNode_ptr node)
+{
+ AreaType material =
+ get_area_type(node->getStringValue("material", "Default"));
+ Point3D p, dummy;
+ const char * s = node->getStringValue("v");
+ s = parse_point(s, p);
+ if (s == 0) {
+ SG_LOG(SG_TERRAIN, SG_WARN, "No point supplied; skipped");
+ return;
+ }
+ s = parse_point(s, dummy);
+ if (s != 0)
+ SG_LOG(SG_TERRAIN, SG_WARN, "More than one vertex supplied for point");
+ FGPolygon poly;
+ makePolygon(p, node->getIntValue("width", 500), poly);
+ poly = polygon_int(poly, bounds_poly);
+ split_polygon(".", material, poly);
+}
+
+static void
+add_line (SGPropertyNode_ptr node)
+{
+ AreaType material =
+ get_area_type(node->getStringValue("material", "Default"));
+ const char * s = node->getStringValue("v");
+
+ Point3D p;
+ Line line;
+ s = parse_point(s, p);
+ while (s != 0) {
+ line.addPoint(p);
+ s = parse_point(s, p);
+ }
+
+ FGPolygon poly;
+ makePolygon(line, node->getIntValue("width", 10), poly);
+ poly = polygon_int(poly, bounds_poly);
+ split_polygon(".", material, poly);
+}
+
+static void
+add_polygon (SGPropertyNode_ptr node)
+{
+ FGPolygon poly;
+ AreaType material =
+ get_area_type(node->getStringValue("material", "Default"));
+ vector contour_nodes = node->getChildren("contour");
+ for (int i = 0; i < contour_nodes.size(); i++) {
+ SGPropertyNode_ptr contour_node = contour_nodes[i];
+ Point3D p;
+ const char * s = contour_node->getStringValue("v");
+ s = parse_point(s, p);
+ while (s != 0) {
+ poly.add_node(i, p);
+ s = parse_point(s, p);
+ }
+ poly.set_hole_flag(i, contour_node->getBoolValue("hole", false));
+ }
+ poly = polygon_int(poly, bounds_poly);
+ split_polygon(".", material, poly);
+}
+
+void
+usage ()
+{
+ SG_LOG(SG_TERRAIN, SG_ALERT, "Usage: " << prog_name << " [opts] ");
+ exit(2);
+}
+
+int
+main (int ac, char ** av)
+{
+ sglog().setLogLevels( SG_ALL, SG_DEBUG );
+
+ poly_index_init( "../poly_counter" );
+
+ prog_name = av[0];
+
+ //
+ // Process command-line options.
+ //
+ int argPos = 1;
+ while (argPos < ac) {
+ string arg = av[argPos];
+
+ if (arg.find("--chunk=") == 0) {
+ bounds = parseChunk(arg.substr(8));
+ argPos++;
+ }
+
+ else if (arg.find("--min-lon=") == 0) {
+ bounds.getMin().setx(strtod(arg.substr(10).c_str(), 0));
+ argPos++;
+ }
+
+ else if (arg.find("--min-lat=") == 0) {
+ bounds.getMin().sety(strtod(arg.substr(10).c_str(), 0));
+ argPos++;
+ }
+
+ else if (arg.find("--max-lon=") == 0) {
+ bounds.getMax().setx(strtod(arg.substr(10).c_str(), 0));
+ argPos++;
+ }
+
+ else if (arg.find("--max-lat=") == 0) {
+ bounds.getMax().sety(strtod(arg.substr(10).c_str(), 0));
+ argPos++;
+ }
+
+ else if (arg.find("--work-dir=") == 0) {
+ work_dir = arg.substr(11);
+ argPos++;
+ }
+
+ else if (arg == "--") {
+ argPos++;
+ break;
+ }
+
+ else if (arg.find("-") == 0) {
+ cerr << "Unrecognized option: " << arg << endl;
+ usage();
+ }
+
+ else {
+ break;
+ }
+ }
+
+ bounds.sanify();
+ bounds_poly = bounds.toPoly();
+
+ SGPropertyNode props;
+ if (argPos == ac) {
+ usage();
+ return 2;
+ }
+
+ for (int i = argPos; i < ac; i++) {
+ try {
+ readProperties(av[i], &props);
+ } catch (const sg_throwable &ex) {
+ SG_LOG(SG_TERRAIN, SG_ALERT, "Fatal error loading " << av[1] << ": "
+ << ex.getFormattedMessage());
+ return 1;
+ }
+
+ int nChildren = props.nChildren();
+ for (int j = 0; j < nChildren; j++) {
+ SGPropertyNode_ptr child = props.getChild(j);
+ if (!strcmp("point", child->getName()))
+ add_point(child);
+ else if (!strcmp("line", child->getName()))
+ add_line(child);
+ else if (!strcmp("polygon", child->getName()))
+ add_polygon(child);
+ else
+ SG_LOG(SG_TERRAIN, SG_WARN, "Unrecognized shape type "
+ << child->getName());
+ }
+ }
+}
+