Skip to content

v0.2.54..v0.2.55 changeset ggdm30.js

Garret Voltz edited this page Aug 14, 2020 · 1 revision
diff --git a/translations/ggdm30.js b/translations/ggdm30.js
index 8a10be4..a6cdc96 100644
--- a/translations/ggdm30.js
+++ b/translations/ggdm30.js
@@ -36,2439 +36,2458 @@
 */
 
 ggdm30 = {
-    // getDbSchema - Load the standard schema or modify it into the Thematic structure
-    getDbSchema: function() {
-        layerNameLookup = {}; // <GLOBAL> Lookup table for converting an FCODE to a layername
-        ggdmAttrLookup = {}; // <GLOBAL> Lookup table for checking what attrs are in an FCODE
+  // getDbSchema - Load the standard schema or modify it into the Thematic structure
+  getDbSchema: function() {
+    layerNameLookup = {}; // <GLOBAL> Lookup table for converting an FCODE to a layername
+    ggdmAttrLookup = {}; // <GLOBAL> Lookup table for checking what attrs are in an FCODE
 
-        // Warning: This is <GLOBAL> so we can get access to it from other functions
-        ggdm30.rawSchema = ggdm30.schema.getDbSchema();
+    // Warning: This is <GLOBAL> so we can get access to it from other functions
+    ggdm30.rawSchema = ggdm30.schema.getDbSchema();
 
-        // Add the Very ESRI specific FCSubtype attribute
-        if (config.getOgrEsriFcsubtype() == 'true') ggdm30.rawSchema = translate.addFCSubtype(ggdm30.rawSchema);
+    // Add the Very ESRI specific FCSubtype attribute
+    if (config.getOgrEsriFcsubtype() == 'true') ggdm30.rawSchema = translate.addFCSubtype(ggdm30.rawSchema);
 
-        // Add empty "extra" feature layers if needed
-        if (config.getOgrNoteExtra() == 'file') ggdm30.rawSchema = translate.addExtraFeature(ggdm30.rawSchema);
+    // Add empty "extra" feature layers if needed
+    if (config.getOgrNoteExtra() == 'file') ggdm30.rawSchema = translate.addExtraFeature(ggdm30.rawSchema);
 
-        // Build the GGDM fcode/attrs lookup table. Note: This is <GLOBAL>
-        ggdmAttrLookup = translate.makeAttrLookup(ggdm30.rawSchema);
+    // Build the GGDM fcode/attrs lookup table. Note: This is <GLOBAL>
+    ggdmAttrLookup = translate.makeAttrLookup(ggdm30.rawSchema);
 
-        // Debug:
-        // print("ggdmAttrLookup: Start");
-        // translate.dumpLookup(ggdmAttrLookup);
-        // print("ggdmAttrLookup: End");
+    // Debug:
+    // print("ggdmAttrLookup: Start");
+    // translate.dumpLookup(ggdmAttrLookup);
+    // print("ggdmAttrLookup: End");
 
-        // Decide if we are going to use the Thematic structure or 1 FCODE / File
-        // if we DON't want the new structure, just return the ggdm30.rawSchema
-        if (config.getOgrThematicStructure() == 'false')
-        {
-            // Now build the FCODE/layername lookup table. Note: This is <GLOBAL>
-            layerNameLookup = translate.makeLayerNameLookup(ggdm30.rawSchema);
+    // Decide if we are going to use the Thematic structure or 1 FCODE / File
+    // if we DON't want the new structure, just return the ggdm30.rawSchema
+    if (config.getOgrThematicStructure() == 'false')
+    {
+      // Now build the FCODE/layername lookup table. Note: This is <GLOBAL>
+      layerNameLookup = translate.makeLayerNameLookup(ggdm30.rawSchema);
 
-            // Now add an o2s[A,L,P] feature to the ggdm30.rawSchema
-            // We can drop features but this is a nice way to see what we would drop
-            ggdm30.rawSchema = translate.addEmptyFeature(ggdm30.rawSchema);
+      // Now add an o2s[A,L,P] feature to the ggdm30.rawSchema
+      // We can drop features but this is a nice way to see what we would drop
+      ggdm30.rawSchema = translate.addEmptyFeature(ggdm30.rawSchema);
 
-            // Add the empty Review layers
-            ggdm30.rawSchema = translate.addReviewFeature(ggdm30.rawSchema);
+      // Add the empty Review layers
+      ggdm30.rawSchema = translate.addReviewFeature(ggdm30.rawSchema);
 
-            // Debugging:
-            // translate.dumpSchema(ggdm30.rawSchema);
+      // Debugging:
+      // translate.dumpSchema(ggdm30.rawSchema);
 
-            return ggdm30.rawSchema;
-        }
+      return ggdm30.rawSchema;
+    }
 
-        // OK, now we build a new schema
-        var newSchema = [];
-        var layerName = '';
-        var fCode = '';
+    // OK, now we build a new schema
+    var newSchema = [];
+    var layerName = '';
+    var fCode = '';
 
-        // Go through the fcode/layer list, find all of the layers and build a skeleton schema
-        // layerList is used to keep track of what we have already seen
-        var layerList = [];
-        var geomType = '';
-        for (var fc in ggdm30.rules.thematicGroupList)
-        {
-            layerName = ggdm30.rules.thematicGroupList[fc];
-            if (~layerList.indexOf(layerName)) continue;  // Funky use of ~ instead of '!== -1'
-            layerList.push(layerName);
+    // Go through the fcode/layer list, find all of the layers and build a skeleton schema
+    // layerList is used to keep track of what we have already seen
+    var layerList = [];
+    var geomType = '';
+    for (var fc in ggdm30.rules.thematicGroupList)
+    {
+      layerName = ggdm30.rules.thematicGroupList[fc];
+      if (~layerList.indexOf(layerName)) continue;  // Funky use of ~ instead of '!== -1'
+      layerList.push(layerName);
+
+      // Now build a skeleton schema
+      if (~layerName.indexOf('Pnt'))
+      {
+        geomType = 'Point';
+      }
+      else if (~layerName.indexOf('Srf'))
+      {
+        geomType = 'Area';
+      }
+      else
+      {
+        geomType = 'Line';
+      }
+
+      newSchema.push({ name: layerName,
+        desc: layerName,
+        geom: geomType,
+        columns:[]
+      });
+    } // End fc loop
+
+    // Loop through the old schema and populate the new one
+    var newSchemaLen = newSchema.length; // cached as we use this a lot
+    for (var os = 0, osLen = ggdm30.rawSchema.length; os < osLen; os++)
+    {
+      // The table looks like:
+      // 'PGB230':'AeronauticPnt', // AircraftHangar
+      // 'AGB230':'AeronauticSrf', // AircraftHangar
+      // 'AGB015':'AeronauticSrf', // Apron
+      // ....
+      // So we add the geometry to the FCODE
+
+      fCode = ggdm30.rawSchema[os].geom.charAt(0) + ggdm30.rawSchema[os].fcode;
+      layerName = ggdm30.rules.thematicGroupList[fCode];
+
+      // Loop through the new schema and find the right layer
+      for (var ns = 0; ns < newSchemaLen; ns++)
+      {
+        // If we find the layer, populate it
+        if (newSchema[ns].name == layerName)
+        {
+          // now start adding attrs from the raw schema. This Is Not Pretty
+
+          // Loop through the columns in the OLD schema
+          for (var cos = 0, cosLen = ggdm30.rawSchema[os].columns.length; cos < cosLen; cos++)
+          {
+            var same = false;
+            // Loop through the columns in the NEW schema
+            for (var cns = 0, cnsLen = newSchema[ns].columns.length; cns < cnsLen; cns++)
+            {
+              // If the attribute names match then we can ignore it, unless it is enumerated
+              if (ggdm30.rawSchema[os].columns[cos].name == newSchema[ns].columns[cns].name)
+              {
+                same = true;
+                if (ggdm30.rawSchema[os].columns[cos].type !== 'enumeration' ) break;
+
+                // Now for some more uglyness....
+                // loop through the enumerated values  in the OLD schema
+                for (var oen = 0, oenlen = ggdm30.rawSchema[os].columns[cos].enumerations.length; oen < oenlen; oen++)
+                {
+                  var esame = false;
+                  // Loop through the enumerated values in the NEW schema
+                  for (var nen = 0, nenlen = newSchema[ns].columns[cns].enumerations.length; nen < nenlen; nen++)
+                  {
+                    // If the names match, ignore it
+                    if (ggdm30.rawSchema[os].columns[cos].enumerations[oen].name == newSchema[ns].columns[cns].enumerations[nen].name)
+                    {
+                      esame = true;
+                      break;
+                    }
+                  } // End nen loop
+                  // if the enumerated value isn't in the new list, add it
+                  if (!esame)
+                  {
+                    newSchema[ns].columns[cns].enumerations.push(ggdm30.rawSchema[os].columns[cos].enumerations[oen]);
+                  }
+                } // End oen loop
+              } // End if enumeration
+            } // End nsc loop
 
-            // Now build a skeleton schema
-            if (~layerName.indexOf('Pnt'))
-            {
-                geomType = 'Point';
-            }
-            else if (~layerName.indexOf('Srf'))
+            // if the attr isn't in the new schema, add it
+            if (!same)
             {
-                geomType = 'Area';
-            }
-            else
-            {
-                geomType = 'Line';
+              // Remove the Default Value so we get all Null values on export
+              // delete ggdm30.rawSchema[os].columns[cos].defValue;
+              //ggdm30.rawSchema[os].columns[cos].defValue = undefined;
+
+              newSchema[ns].columns.push(ggdm30.rawSchema[os].columns[cos]);
             }
+          } // End osc loop
+        } // End if layerName
+      } // End newSchema loop
+    } // end ggdm30.rawSchema loop
 
-            newSchema.push({ name: layerName,
-                          desc: layerName,
-                          geom: geomType,
-                          columns:[]
-                        });
-        } // End fc loop
+    // Create a lookup table of TDS like structures attributes. Note this is <GLOBAL>
+    ggdmThematicLookup = translate.makeTdsAttrLookup(newSchema);
 
-        // Loop through the old schema and populate the new one
-        var newSchemaLen = newSchema.length; // cached as we use this a lot
-        for (var os = 0, osLen = ggdm30.rawSchema.length; os < osLen; os++)
-        {
-            // The table looks like:
-            // 'PGB230':'AeronauticPnt', // AircraftHangar
-            // 'AGB230':'AeronauticSrf', // AircraftHangar
-            // 'AGB015':'AeronauticSrf', // Apron
-            // ....
-            // So we add the geometry to the FCODE
-
-            fCode = ggdm30.rawSchema[os].geom.charAt(0) + ggdm30.rawSchema[os].fcode;
-            layerName = ggdm30.rules.thematicGroupList[fCode];
-
-            // Loop through the new schema and find the right layer
-            for (var ns = 0; ns < newSchemaLen; ns++)
-            {
-                // If we find the layer, populate it
-                if (newSchema[ns].name == layerName)
-                {
-                    // now start adding attrs from the raw schema. This Is Not Pretty
+    // Debug:
+    //print("ggdmThematicLookup: Start");
+    //translate.dumpLookup(ggdmThematicLookup);
+    //print("ggdmThematicLookup: End");
 
-                    // Loop through the columns in the OLD schema
-                    for (var cos = 0, cosLen = ggdm30.rawSchema[os].columns.length; cos < cosLen; cos++)
-                    {
-                        var same = false;
-                        // Loop through the columns in the NEW schema
-                        for (var cns = 0, cnsLen = newSchema[ns].columns.length; cns < cnsLen; cns++)
-                        {
-                            // If the attribute names match then we can ignore it, unless it is enumerated
-                            if (ggdm30.rawSchema[os].columns[cos].name == newSchema[ns].columns[cns].name)
-                            {
-                                same = true;
-                                if (ggdm30.rawSchema[os].columns[cos].type !== 'enumeration' ) break;
-
-                                // Now for some more uglyness....
-                                // loop through the enumerated values  in the OLD schema
-                                for (var oen = 0, oenlen = ggdm30.rawSchema[os].columns[cos].enumerations.length; oen < oenlen; oen++)
-                                {
-                                    var esame = false;
-                                    // Loop through the enumerated values in the NEW schema
-                                    for (var nen = 0, nenlen = newSchema[ns].columns[cns].enumerations.length; nen < nenlen; nen++)
-                                    {
-                                        // If the names match, ignore it
-                                        if (ggdm30.rawSchema[os].columns[cos].enumerations[oen].name == newSchema[ns].columns[cns].enumerations[nen].name)
-                                        {
-                                            esame = true;
-                                            break;
-                                        }
-                                    } // End nen loop
-                                    // if the enumerated value isn't in the new list, add it
-                                    if (!esame)
-                                    {
-                                        newSchema[ns].columns[cns].enumerations.push(ggdm30.rawSchema[os].columns[cos].enumerations[oen]);
-                                    }
-                                } // End oen loop
-                            } // End if enumeration
-                        } // End nsc loop
-
-                        // if the attr isn't in the new schema, add it
-                        if (!same)
-                        {
-                            // Remove the Default Value so we get all Null values on export
-                            // delete ggdm30.rawSchema[os].columns[cos].defValue;
-                            //ggdm30.rawSchema[os].columns[cos].defValue = undefined;
-
-                            newSchema[ns].columns.push(ggdm30.rawSchema[os].columns[cos]);
-                        }
-                    } // End osc loop
-                } // End if layerName
-            } // End newSchema loop
-        } // end ggdm30.rawSchema loop
-
-        // Create a lookup table of TDS like structures attributes. Note this is <GLOBAL>
-        ggdmThematicLookup = translate.makeTdsAttrLookup(newSchema);
-
-        // Debug:
-        //print("ggdmThematicLookup: Start");
-        //translate.dumpLookup(ggdmThematicLookup);
-        //print("ggdmThematicLookup: End");
-
-        // Add the ESRI Feature Dataset name to the schema
-        if (config.getOgrEsriFdname() !== "") newSchema = translate.addFdName(newSchema,config.getOgrEsriFdname());
-
-        // Now add the o2s feature to the ggdm30.rawSchema
-        // We can drop features but this is a nice way to see what we would drop
-        // NOTE: We add these feature AFTER adding the ESRI Feature Dataset so that they
-        // DON'T get put under the Feature Dataset in the output
-        newSchema = translate.addEmptyFeature(newSchema);
-
-        // Add the empty Review layers
-        newSchema = translate.addReviewFeature(newSchema);
-
-        // Debug:
-        // print("newSchema: Start");
-        // translate.dumpSchema(newSchema);
-        // print("newSchema: End");
-
-        return newSchema;
-
-    }, // End getDbSchema
-
-    // validateAttrs: Clean up the supplied attr list by dropping anything that should not be part of the
-    //                feature, checking values and populateing the OTH field
-    validateAttrs: function(geometryType,attrs) {
-
-        // First, use the lookup table to quickly drop all attributes that are not part of the feature
-        // This is quicker than going through the Schema due to the way the Schema is arranged
-        var attrList = ggdmAttrLookup[geometryType.toString().charAt(0) + attrs.F_CODE];
-
-        var othList = {};
-
-        if (attrs.OTH)
-        {
-            othList = translate.parseOTH(attrs.OTH); // Unpack the OTH field
-            delete attrs.OTH;
-        }
+    // Add the ESRI Feature Dataset name to the schema
+    if (config.getOgrEsriFdname() !== '') newSchema = translate.addFdName(newSchema,config.getOgrEsriFdname());
 
-        if (attrList != undefined)
-        {
-            for (var val in attrs)
-            {
-                if (attrList.indexOf(val) == -1)
-                {
-                    if (val in othList)
-                    {
-                        //Debug:
-                        // print('Validate: Dropping OTH: ' + val + '  (' + othList[val] + ')');
-                        delete othList[val];
-                    }
+    // Now add the o2s feature to the ggdm30.rawSchema
+    // We can drop features but this is a nice way to see what we would drop
+    // NOTE: We add these feature AFTER adding the ESRI Feature Dataset so that they
+    // DON'T get put under the Feature Dataset in the output
+    newSchema = translate.addEmptyFeature(newSchema);
 
-                    // Debug:
-                    hoot.logDebug('Validate: Dropping ' + val + '  from ' + attrs.F_CODE);
-                    delete attrs[val];
+    // Add the empty Review layers
+    newSchema = translate.addReviewFeature(newSchema);
 
-                    // Since we deleted the attribute, Skip the text check
-                    continue;
-                }
+    // Debug:
+    // print("newSchema: Start");
+    // translate.dumpSchema(newSchema);
+    // print("newSchema: End");
 
-                // Now check the length of the text fields
-                // We need more info from the customer about this: What to do if it is too long
-                if (val in ggdm30.rules.txtLength)
-                {
-                    if (attrs[val].length > ggdm30.rules.txtLength[val])
-                    {
-                        // First try splitting the attribute and grabbing the first value
-                        var tStr = attrs[val].split(';');
-                        if (tStr[0].length <= ggdm30.rules.txtLength[val])
-                        {
-                            attrs[val] = tStr[0];
-                        }
-                        else
-                        {
-                            hoot.logDebug('Validate: Attribute ' + val + ' is ' + attrs[val].length + ' characters long. Truncateing to ' + ggdm30.rules.txtLength[val] + ' characters.');
-                            // Still too long. Chop to the maximum length
-                            attrs[val] = tStr[0].substring(0,ggdm30.rules.txtLength[val]);
-                        }
-                    } // End text attr length > max length
-
-                    // It's text fo skip the next test
-                    continue;
-                } // End in txtLength
-            } // End attrs loop
-        }
-        else
-        {
-            hoot.logDebug('Validate: No attrList for ' + attrs.F_CODE + ' ' + geometryType);
-        } // End Drop attrs
+    return newSchema;
 
-        // Repack the OTH field
-        if (Object.keys(othList).length > 0)
-        {
-            attrs.OTH = translate.packOTH(othList);
-            // Debug:
-            // print('New OTH: ' + attrs.OTH);
-        }
+  }, // End getDbSchema
 
-        // No quick and easy way to do this unless we build yet another lookup table
-        var feature = {};
+  // validateAttrs: Clean up the supplied attr list by dropping anything that should not be part of the
+  //                feature, checking values and populateing the OTH field
+  validateAttrs: function(geometryType,attrs,notUsed,transMap) {
 
-        for (var i=0, sLen = ggdm30.rawSchema.length; i < sLen; i++)
-        {
-            if (ggdm30.rawSchema[i].fcode == attrs.F_CODE && ggdm30.rawSchema[i].geom == geometryType)
-            {
-                feature = ggdm30.rawSchema[i];
-                break;
-            }
-        }
+    // First, use the lookup table to quickly drop all attributes that are not part of the feature
+    // This is quicker than going through the Schema due to the way the Schema is arranged
+    var attrList = ggdmAttrLookup[geometryType.toString().charAt(0) + attrs.F_CODE];
 
-        // Now validate the Enumerated values
-        for (var i=0, cLen = feature['columns'].length; i < cLen; i++)
-        {
-            // Skip non enumeratied attributes
-            if (feature.columns[i].type !== 'enumeration') continue;
+    var othList = {};
 
-            var enumName = feature.columns[i].name;
+    if (attrs.OTH)
+    {
+      othList = translate.parseOTH(attrs.OTH); // Unpack the OTH field
+      delete attrs.OTH;
+    }
+
+    if (attrList != undefined)
+    {
+      for (var val in attrs)
+      {
+        if (attrList.indexOf(val) == -1)
+        {
+          if (val in othList)
+          {
+            //Debug:
+            // print('Validate: Dropping OTH: ' + val + '  (' + othList[val] + ')');
+            delete othList[val];
+          }
 
-            // Skip stuff that is missing and will end up as a default value
-            if (!(attrs[enumName])) continue;
+          if (val in transMap)
+          {
+            notUsed[transMap[val][1]] = transMap[val][2];
+            // Debug:
+            // print('Validate: Re-Adding ' + transMap[val][1] + ' = ' + transMap[val][2] + ' to notUsed');
+            // print('Validate: Dropping ' + val + ' = ' + attrs[val] + ' from ' + attrs.F_CODE);
+          }
 
-            var attrValue = attrs[enumName];
-            var enumList = feature.columns[i].enumerations;
-            var enumValueList = [];
+          // Debug:
+          hoot.logDebug('Validate: Dropping ' + val + ' = ' + attrs[val] + ' from ' + attrs.F_CODE);
+          delete attrs[val];
 
-            // Pull all of the values out of the enumerated list to make life easier
-            for (var j=0, elen = enumList.length; j < elen; j++) enumValueList.push(enumList[j].value);
+          // Since we deleted the attribute, Skip the text check
+          continue;
+        }
 
-            // If we DONT have the value in the list, add it to the OTH or MEMO field
-            if (enumValueList.indexOf(attrValue) == -1)
+        // Now check the length of the text fields
+        // We need more info from the customer about this: What to do if it is too long
+        if (val in ggdm30.rules.txtLength)
+        {
+          if (attrs[val].length > ggdm30.rules.txtLength[val])
+          {
+            // First try splitting the attribute and grabbing the first value
+            var tStr = attrs[val].split(';');
+            if (tStr[0].length <= ggdm30.rules.txtLength[val])
             {
-                var othVal = '(' + enumName + ':' + attrValue + ')';
+              attrs[val] = tStr[0];
+            }
+            else
+            {
+              hoot.logDebug('Validate: Attribute ' + val + ' is ' + attrs[val].length + ' characters long. Truncateing to ' + ggdm30.rules.txtLength[val] + ' characters.');
+              // Still too long. Chop to the maximum length
+              attrs[val] = tStr[0].substring(0,ggdm30.rules.txtLength[val]);
+            }
+          } // End text attr length > max length
 
-                // No "Other" value. Push to the Memo field
-                if (enumValueList.indexOf('999') == -1)
-                {
-                    // Set the offending enumerated value to the default value
-                    attrs[enumName] = feature.columns[i].defValue;
+          // It's text fo skip the next test
+          continue;
+        } // End in txtLength
+      } // End attrs loop
+    }
+    else
+    {
+      hoot.logDebug('Validate: No attrList for ' + attrs.F_CODE + ' ' + geometryType);
+    } // End Drop attrs
 
-                    hoot.logTrace('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting ' + enumName + ' to its default value (' + feature.columns[i].defValue + ')');
+    // Repack the OTH field
+    if (Object.keys(othList).length > 0)
+    {
+      attrs.OTH = translate.packOTH(othList);
+      // Debug:
+      // print('New OTH: ' + attrs.OTH);
+    }
 
-                    attrs.ZI006_MEM = translate.appendValue(attrs.ZI006_MEM,othVal,';');
-                }
-                else
-                {
-                    // Set the offending enumerated value to the "other" value
-                    attrs[enumName] = '999';
+    // No quick and easy way to do this unless we build yet another lookup table
+    var feature = {};
 
-                    hoot.logTrace('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting OTH and ' + enumName + ' to Other (999)');
+    for (var i=0, sLen = ggdm30.rawSchema.length; i < sLen; i++)
+    {
+      if (ggdm30.rawSchema[i].fcode == attrs.F_CODE && ggdm30.rawSchema[i].geom == geometryType)
+      {
+        feature = ggdm30.rawSchema[i];
+        break;
+      }
+    }
+
+    // Now validate the Enumerated values
+    for (var i=0, cLen = feature['columns'].length; i < cLen; i++)
+    {
+      // Skip non enumeratied attributes
+      if (feature.columns[i].type !== 'enumeration') continue;
 
-                    attrs.OTH = translate.appendValue(attrs.OTH,othVal,' ');
-                }
+      var enumName = feature.columns[i].name;
 
-            } // End attrValue in enumList
+      // Skip stuff that is missing and will end up as a default value
+      if (!(attrs[enumName])) continue;
 
-        } // End Validate Enumerations
+      var attrValue = attrs[enumName];
+      var enumList = feature.columns[i].enumerations;
+      var enumValueList = [];
 
-    }, // End validateAttrs
+      // Pull all of the values out of the enumerated list to make life easier
+      for (var j=0, elen = enumList.length; j < elen; j++) enumValueList.push(enumList[j].value);
 
+      // If we DONT have the value in the list, add it to the OTH or MEMO field
+      if (enumValueList.indexOf(attrValue) == -1)
+      {
+        var othVal = '(' + enumName + ':' + attrValue + ')';
 
-    // validateTDSAttrs - Clean up the TDS format attrs.  This sets all of the extra attrs to be "undefined"
-    validateTDSAttrs: function(gFcode, attrs) {
+        // No "Other" value. Push to the Memo field
+        if (enumValueList.indexOf('999') == -1)
+        {
+          // Set the offending enumerated value to the default value
+          attrs[enumName] = feature.columns[i].defValue;
 
-        var thematicAttrList = ggdmThematicLookup[ggdm30.rules.thematicGroupList[gFcode]];
-        var ggdmAttrList = ggdmAttrLookup[gFcode];
+          hoot.logTrace('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting ' + enumName + ' to its default value (' + feature.columns[i].defValue + ')');
 
-        for (var i = 0, len = thematicAttrList.length; i < len; i++)
-        {
-            if (ggdmAttrList.indexOf(thematicAttrList[i]) == -1) attrs[thematicAttrList[i]] = undefined;
+          attrs.ZI006_MEM = translate.appendValue(attrs.ZI006_MEM,othVal,';');
         }
-    }, // End validateTDSAttrs
+        else
+        {
+          // Set the offending enumerated value to the "other" value
+          attrs[enumName] = '999';
 
+          hoot.logTrace('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting OTH and ' + enumName + ' to Other (999)');
 
-    // Sort out if we need to return more than one feature
-    // This is generally for Roads, Railways, bridges, tunnels etc
-    manyFeatures: function(geometryType, tags, attrs)
-    {
+          attrs.OTH = translate.appendValue(attrs.OTH,othVal,' ');
+        }
 
-        // Add the first feature to the structure that we return
-        var returnData = [{attrs:attrs, tableName:''}];
+      } // End attrValue in enumList
 
-        // Quit early if we don't need to check anything. We are only looking at linework
-        if (geometryType !== 'Line') return returnData;
+    } // End Validate Enumerations
 
-        // Only looking at roads & railways with something else tacked on
-        if (!(tags.highway || tags.railway)) return returnData;
+  }, // End validateAttrs
 
-        // Check the list of secondary/tertiary etc features
-        if (!(tags.bridge || tags.tunnel || tags.embankment || tags.cutting || tags.ford)) return returnData;
 
-        // We are going to make another feature so copy tags and trash the UUID so it gets a new one
-        var newFeatures = [];
-        var newAttributes = {};
-        var nTags = JSON.parse(JSON.stringify(tags));
-        delete nTags.uuid;
-        delete nTags['hoot:id'];
+  // validateTDSAttrs - Clean up the TDS format attrs.  This sets all of the extra attrs to be "undefined"
+  validateTDSAttrs: function(gFcode, attrs) {
 
-        // Now drop the tags that made the FCODE
-        switch(attrs.F_CODE)
-        {
-            case 'AN010': // Railway
-            case 'AN050': // Railway Sidetrack
-                delete nTags.railway;
-                newAttributes.TRS = '12'; // Transport Type = Railway
-                break;
-
-            case 'AP010': // Cart Track
-            case 'AP030': // Road
-            case 'AP050': // Trail
-                switch (nTags.highway)
-                {
-                    case 'pedestrian':
-                    case 'footway':
-                    case 'steps':
-                    case 'path':
-                    case 'bridleway':
-                    case 'cycleway':
-                        newAttributes.TRS = '9'; // Transport Type = Pedestrian
-                        break;
-
-                    default:
-                        newAttributes.TRS = '13'; // Transport Type = Road
-                }
-                delete nTags.highway;
-                break;
-
-            case 'AQ040': // Bridge
-                delete nTags.bridge;
-                newAttributes.SBB = '1001'; // Supported By Bridge Span = True
-                break;
-
-            case 'AQ130': // Tunnel
-                delete nTags.tunnel;
-                newAttributes.CWT = '1001'; // Contained Within Tunnel = True
-                break;
-
-            case 'BH070': // Ford
-                delete nTags.ford;
-                break;
-
-            case 'DB070': // Cutting
-                delete nTags.cutting;
-                break;
-
-            case 'DB090': // Embankment
-                delete nTags.embankment;
-                break;
-
-            default:
-                // Debug
-                hoot.logWarn('ManyFeatures: Should get to here');
-        } // end switch
-
-        // Now make new features based on what tags are left
-        if (nTags.railway)
-        {
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.railway;
-        }
+    var thematicAttrList = ggdmThematicLookup[ggdm30.rules.thematicGroupList[gFcode]];
+    var ggdmAttrList = ggdmAttrLookup[gFcode];
 
-        if (nTags.highway)
-        {
-            if (nTags.highway == 'track') newAttributes.TRS = '3'; // Cart Track TRS = Automotive
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.highway;
-        }
+    for (var i = 0, len = thematicAttrList.length; i < len; i++)
+    {
+      if (ggdmAttrList.indexOf(thematicAttrList[i]) == -1) attrs[thematicAttrList[i]] = undefined;
+    }
+  }, // End validateTDSAttrs
 
-        if (nTags.cutting)
-        {
-            newAttributes.F_CODE = 'DB070';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.cutting;
-        }
 
-        if (nTags.bridge && nTags.bridge !== 'no')
-        {
-            newAttributes.F_CODE = 'AQ040';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.bridge;
-        }
+  // Sort out if we need to return more than one feature
+  // This is generally for Roads, Railways, bridges, tunnels etc
+  manyFeatures: function(geometryType, tags, attrs,transMap)
+  {
 
-        if (nTags.tunnel && nTags.tunnel !== 'no')
-        {
-            newAttributes.F_CODE = 'AQ130';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.tunnel;
-        }
+    // Add the first feature to the structure that we return
+    var returnData = [{attrs:attrs, tableName:''}];
 
-        if (nTags.ford && nTags.ford !== 'no')
-        {
-            newAttributes.F_CODE = 'BH070';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.ford;
-        }
+    // Quit early if we don't need to check anything. We are only looking at linework
+    if (geometryType !== 'Line') return returnData;
 
-        if (nTags.embankment)
-        {
-            newAttributes.F_CODE = 'DB090';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.embankment;
-        }
+    // Only looking at roads & railways with something else tacked on
+    if (!(tags.highway || tags.railway)) return returnData;
 
-        // Loop through the new features and process them
-        for (var i = 0, nFeat = newFeatures.length; i < nFeat; i++)
-        {
-            // pre processing
-            ggdm30.applyToOgrPreProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType);
+    // Check the list of secondary/tertiary etc features
+    if (!(tags.bridge || tags.tunnel || tags.embankment || tags.cutting || tags.ford)) return returnData;
 
-            // apply the simple number and text biased rules
-            // Note: These are BACKWARD, not forward!
-            translate.applySimpleNumBiased(newFeatures[i]['attrs'], newFeatures[i]['tags'], ggdm30.rules.numBiased,'backward',ggdm30.rules.intList);
-            translate.applySimpleTxtBiased(newFeatures[i]['attrs'], newFeatures[i]['tags'], ggdm30.rules.txtBiased,'backward');
+    // We are going to make another feature so copy tags and trash the UUID so it gets a new one
+    var newFeatures = [];
+    var newAttributes = {};
+    var nTags = JSON.parse(JSON.stringify(tags));
+    delete nTags.uuid;
+    delete nTags['hoot:id'];
 
-            // one 2 one - we call the version that knows about OTH fields
-            translate.applyTdsOne2One(newFeatures[i]['tags'], newFeatures[i]['attrs'], ggdm30.lookup, ggdm30.fcodeLookup);
+    // Now drop the tags that made the FCODE
+    switch(attrs.F_CODE)
+    {
+    case 'AN010': // Railway
+    case 'AN050': // Railway Sidetrack
+      delete nTags.railway;
+      newAttributes.TRS = '12'; // Transport Type = Railway
+      break;
+
+    case 'AP010': // Cart Track
+    case 'AP030': // Road
+    case 'AP050': // Trail
+      switch (nTags.highway)
+      {
+      case 'pedestrian':
+      case 'footway':
+      case 'steps':
+      case 'path':
+      case 'bridleway':
+      case 'cycleway':
+        newAttributes.TRS = '9'; // Transport Type = Pedestrian
+        break;
+
+      default:
+        newAttributes.TRS = '13'; // Transport Type = Road
+      }
+      delete nTags.highway;
+      break;
+
+    case 'AQ040': // Bridge
+      delete nTags.bridge;
+      newAttributes.SBB = '1001'; // Supported By Bridge Span = True
+      break;
+
+    case 'AQ130': // Tunnel
+      delete nTags.tunnel;
+      newAttributes.CWT = '1001'; // Contained Within Tunnel = True
+      break;
+
+    case 'BH070': // Ford
+      delete nTags.ford;
+      break;
+
+    case 'DB070': // Cutting
+      delete nTags.cutting;
+      break;
+
+    case 'DB090': // Embankment
+      delete nTags.embankment;
+      break;
+
+    default:
+      // Debug
+      hoot.logWarn('ManyFeatures: Should get to here');
+    } // end switch
+
+    // Now make new features based on what tags are left
+    if (nTags.railway)
+    {
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.railway;
+    }
 
-            // post processing
-            ggdm30.applyToOgrPostProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType, {});
+    if (nTags.highway)
+    {
+      if (nTags.highway == 'track') newAttributes.TRS = '3'; // Cart Track TRS = Automotive
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.highway;
+    }
 
-            returnData.push({attrs: newFeatures[i]['attrs'],tableName: ''});
-        }
+    if (nTags.cutting)
+    {
+      newAttributes.F_CODE = 'DB070';
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.cutting;
+    }
 
-        return returnData;
-    }, // End manyFeatures
+    if (nTags.bridge && nTags.bridge !== 'no')
+    {
+      newAttributes.F_CODE = 'AQ040';
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.bridge;
+    }
 
-    // Doesn't do much but saves typing the same code out a few times in the to GGDM Pre Processing
-    // NOTE if these are points, we drop the railway/highway tags since we can't make transport features out of these
-    fixTransType : function(tags,geometry)
+    if (nTags.tunnel && nTags.tunnel !== 'no')
     {
-        if (tags.railway)
-        {
-            tags['transport:type'] = 'railway';
-            if (geometry == 'Point') delete tags.railway;
-        }
-        else if (tags.highway && ['path','pedestrian','steps','trail'].indexOf(tags.highway) > -1)
-        {
-            tags['transport:type'] = 'pedestrian';
-            if (geometry == 'Point') delete tags.highway;
-        }
-        else if (tags.highway)
-        {
-            tags['transport:type'] = 'road';
-            if (geometry == 'Point') delete tags.highway;
-        }
-    },
+      newAttributes.F_CODE = 'AQ130';
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.tunnel;
+    }
 
+    if (nTags.ford && nTags.ford !== 'no')
+    {
+      newAttributes.F_CODE = 'BH070';
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.ford;
+    }
 
-// #####################################################################################################
-    // ##### Start of the xxToOsmxx Block #####
-    applyToOsmPreProcessing: function(attrs, layerName, geometryType)
+    if (nTags.embankment)
     {
-        // Drop the FCSUBTYPE since we don't use it
-        if (attrs.FCSUBTYPE) delete attrs.FCSUBTYPE;
+      newAttributes.F_CODE = 'DB090';
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.embankment;
+    }
 
-        // List of data values to drop/ignore
-        var ignoreList = { '-999999.0':1,'-999999':1,'noinformation':1 };
+    // Loop through the new features and process them
+    for (var i = 0, nFeat = newFeatures.length; i < nFeat; i++)
+    {
+      // pre processing
+      ggdm30.applyToOgrPreProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType);
 
-        // List of attributes that can't have '0' as a value
-        var noZeroList = ['BNF','DZC','LC1','LC2','LC3','LC4','LTN','NOS','NPL','VST','WD1','WD2','WT2','ZI016_WD1'];
+      // apply the simple number and text biased rules
+      // Note: These are BACKWARD, not forward!
+      translate.numToOgr(newFeatures[i]['attrs'], newFeatures[i]['tags'], ggdm30.rules.numBiased,ggdm30.rules.intList,transMap);
+      translate.txtToOgr(newFeatures[i]['attrs'], newFeatures[i]['tags'], ggdm30.rules.txtBiased,transMap);
 
-        // This is a handy loop. We use it to:
-        // 1) Remove all of the "No Information" and -999999 fields
-        // 2) Convert all of the Attrs to uppercase - if needed
-       for (var col in attrs)
-        {
-            // slightly ugly but we would like to account for: 'No Information','noInformation' etc
-            // First, push to lowercase
-            var attrValue = attrs[col].toString().toLowerCase();
+      // one 2 one - we call the version that knows about OTH fields
+      translate.applyTdsOne2One(newFeatures[i]['tags'], newFeatures[i]['attrs'], ggdm30.lookup, ggdm30.fcodeLookup,transMap);
 
-            // Get rid of the spaces in the text
-            attrValue = attrValue.replace(/\s/g, '');
+      // post processing
+      ggdm30.applyToOgrPostProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType, {});
 
-            // Wipe out the useless values
-            if (attrs[col] == '' || attrValue in ignoreList || attrs[col] in ignoreList)
-            {
-                delete attrs[col]; // debug: Comment this out to leave all of the No Info stuff in for testing
-                continue;
-            }
+      returnData.push({attrs: newFeatures[i]['attrs'],tableName: ''});
+    }
 
-            // Remove attributes with '0' values if they can't be '0'
-            if (noZeroList.indexOf(col) > -1 && attrs[col] == '0')
-            {
-                delete attrs[col];
-                continue;
-            }
+    return returnData;
+  }, // End manyFeatures
 
-            // Push the attribute to upper case - if needed
-            var c = col.toUpperCase();
-            if (c !== col)
-            {
-                attrs[c] = attrs[col];
-                delete attrs[col];
-                col = c; // For the rest of this loop iteration
-            }
+  // Doesn't do much but saves typing the same code out a few times in the to GGDM Pre Processing
+  // NOTE if these are points, we drop the railway/highway tags since we can't make transport features out of these
+  fixTransType : function(tags,geometry)
+  {
+    if (tags.railway)
+    {
+      tags['transport:type'] = 'railway';
+      if (geometry == 'Point') delete tags.railway;
+    }
+    else if (tags.highway && ['path','pedestrian','steps','trail'].indexOf(tags.highway) > -1)
+    {
+      tags['transport:type'] = 'pedestrian';
+      if (geometry == 'Point') delete tags.highway;
+    }
+    else if (tags.highway)
+    {
+      tags['transport:type'] = 'road';
+      if (geometry == 'Point') delete tags.highway;
+    }
+  },
 
-            // Now see if we need to swap attr names
-            if (col in ggdm30.rules.swapListIn)
-            {
-                // print('Swapped: ' + ggdm30.rules.swapListIn[i]); // debug
-                attrs[ggdm30.rules.swapListIn[col]] = attrs[col];
-                delete attrs[col];
-                continue;
-            }
 
-        } // End in attrs loop
+  // #####################################################################################################
+  // ##### Start of the xxToOsmxx Block #####
+  applyToOsmPreProcessing: function(attrs, layerName, geometryType)
+  {
+    // Drop the FCSUBTYPE since we don't use it
+    if (attrs.FCSUBTYPE) delete attrs.FCSUBTYPE;
 
-        // Drop all of the XXX Closure default values IFF the associated attributes are
-        // not set
-        // Doing this after the main cleaning loop so all of the -999999 values are
-        // already gone and we can just check for existance
-        for (var i in ggdm30.rules.closureList)
-        {
-            if (attrs[i])
-            {
-                if (attrs[ggdm30.rules.closureList[i][0]] || attrs[ggdm30.rules.closureList[i][1]])
-                {
-                    continue;
-                }
-                else
-                {
-                    delete attrs[i];
-                }
-            }
-        } // End closureList
+    // List of data values to drop/ignore
+    var ignoreList = { '-999999.0':1,'-999999':1,'noinformation':1 };
 
-        // Now find an F_CODE
-        if (attrs.F_CODE)
-        {
-            // Drop the the "Not Found" F_CODE. This is from the UI
-            if (attrs.F_CODE == 'Not found') delete attrs.F_CODE;
-        }
-        else if (attrs.FCODE)
+    // List of attributes that can't have '0' as a value
+    var noZeroList = ['BNF','DZC','LC1','LC2','LC3','LC4','LTN','NOS','NPL','VST','WD1','WD2','WT2','ZI016_WD1'];
+
+    // This is a handy loop. We use it to:
+    // 1) Remove all of the "No Information" and -999999 fields
+    // 2) Convert all of the Attrs to uppercase - if needed
+    for (var col in attrs)
+    {
+      // slightly ugly but we would like to account for: 'No Information','noInformation' etc
+      // First, push to lowercase
+      var attrValue = attrs[col].toString().toLowerCase();
+
+      // Get rid of the spaces in the text
+      attrValue = attrValue.replace(/\s/g, '');
+
+      // Wipe out the useless values
+      if (attrs[col] == '' || attrValue in ignoreList || attrs[col] in ignoreList)
+      {
+        delete attrs[col]; // debug: Comment this out to leave all of the No Info stuff in for testing
+        continue;
+      }
+
+      // Remove attributes with '0' values if they can't be '0'
+      if (noZeroList.indexOf(col) > -1 && attrs[col] == '0')
+      {
+        delete attrs[col];
+        continue;
+      }
+
+      // Push the attribute to upper case - if needed
+      var c = col.toUpperCase();
+      if (c !== col)
+      {
+        attrs[c] = attrs[col];
+        delete attrs[col];
+        col = c; // For the rest of this loop iteration
+      }
+
+      // Now see if we need to swap attr names
+      if (col in ggdm30.rules.swapListIn)
+      {
+        // print('Swapped: ' + ggdm30.rules.swapListIn[i]); // debug
+        attrs[ggdm30.rules.swapListIn[col]] = attrs[col];
+        delete attrs[col];
+        continue;
+      }
+
+    } // End in attrs loop
+
+    // Drop all of the XXX Closure default values IFF the associated attributes are
+    // not set
+    // Doing this after the main cleaning loop so all of the -999999 values are
+    // already gone and we can just check for existance
+    for (var i in ggdm30.rules.closureList)
+    {
+      if (attrs[i])
+      {
+        if (attrs[ggdm30.rules.closureList[i][0]] || attrs[ggdm30.rules.closureList[i][1]])
         {
-            attrs.F_CODE = attrs.FCODE;
-            delete attrs.FCODE;
+          continue;
         }
         else
         {
-            // Time to find an FCODE based on the filename
-            // Funky but it makes life easier
-            var llayerName = layerName.toString().toLowerCase();
+          delete attrs[i];
+        }
+      }
+    } // End closureList
 
-            for (var row in ggdm30.rules.fCodeMap)
-            {
-                for (var val in ggdm30.rules.fCodeMap[row][1])
-                {
-                    if (llayerName == ggdm30.rules.fCodeMap[row][1][val])
-                    {
-                        attrs.F_CODE = ggdm30.rules.fCodeMap[row][0];
-                        break;
-                    }
-                }
-            }
-        } // End of Find an FCode
+    // Now find an F_CODE
+    if (attrs.F_CODE)
+    {
+      // Drop the the "Not Found" F_CODE. This is from the UI
+      if (attrs.F_CODE == 'Not found') delete attrs.F_CODE;
+    }
+    else if (attrs.FCODE)
+    {
+      attrs.F_CODE = attrs.FCODE;
+      delete attrs.FCODE;
+    }
+    else
+    {
+      // Time to find an FCODE based on the filename
+      // Funky but it makes life easier
+      var llayerName = layerName.toString().toLowerCase();
+
+      for (var row in ggdm30.rules.fCodeMap)
+      {
+        for (var val in ggdm30.rules.fCodeMap[row][1])
+        {
+          if (llayerName == ggdm30.rules.fCodeMap[row][1][val])
+          {
+            attrs.F_CODE = ggdm30.rules.fCodeMap[row][0];
+            break;
+          }
+        }
+      }
+    } // End of Find an FCode
 
-    }, // End of applyToOsmPreProcessing
+  }, // End of applyToOsmPreProcessing
 
 
-// #####################################################################################################
-    applyToOsmPostProcessing : function (attrs, tags, layerName, geometryType)
+  // #####################################################################################################
+  applyToOsmPostProcessing : function (attrs, tags, layerName, geometryType)
+  {
+    // Unpack the MEMO field
+    if (tags.note)
+    {
+      var tObj = translate.unpackMemo(tags.note);
+
+      if (tObj.tags !== '')
+      {
+        var tTags = JSON.parse(tObj.tags);
+        for (i in tTags)
+        {
+          if (tags[tTags[i]]) hoot.logWarn('Unpacking ZI006_MEM, overwriteing ' + i + ' = ' + tags[i] + '  with ' + tTags[i]);
+          tags[i] = tTags[i];
+        }
+      }
+
+      if (tObj.text && tObj.text !== '')
+      {
+        tags.note = tObj.text;
+      }
+      else
+      {
+        delete tags.note;
+      }
+    } // End process tags.note
+
+    // Roads. GGDM30 is the same as TDSv61
+    if (attrs.F_CODE == 'AP030' || attrs.F_CODE == 'AQ075') // Road & Ice Road
     {
-        // Unpack the MEMO field
-        if (tags.note)
+      // Set a Default: "It is a road but we don't know what it is"
+      tags.highway = 'road';
+
+      // Top level
+      if (tags['ref:road:type'] == 'motorway' || tags['ref:road:class'] == 'national_motorway')
+      {
+        tags.highway = 'motorway';
+      }
+      else if (tags['ref:road:type'] == 'limited_access_motorway')
+      {
+        tags.highway = 'trunk';
+      }
+      else if (tags['ref:road:class'] == 'primary')
+      {
+        tags.highway = 'primary';
+      }
+      else if (tags['ref:road:class'] == 'secondary')
+      {
+        tags.highway = 'secondary';
+      }
+      else if (tags['ref:road:class'] == 'local')
+      {
+        if (tags['ref:road:type'] == 'road') // RTY=3
+        {
+          tags.highway = 'tertiary';
+        }
+        else
         {
-            var tObj = translate.unpackMemo(tags.note);
-
-            if (tObj.tags !== '')
-            {
-                var tTags = JSON.parse(tObj.tags)
-                for (i in tTags)
-                {
-                    if (tags[tTags[i]]) hoot.logWarn('Unpacking ZI006_MEM, overwriteing ' + i + ' = ' + tags[i] + '  with ' + tTags[i]);
-                    tags[i] = tTags[i];
-                }
-            }
+          tags.highway = 'unclassified';
+        }
+      }
+      else if (tags['ref:road:type'] == 'pedestrian')
+      {
+        tags.highway = 'pedestrian';
+      }
+      else if (tags['ref:road:type'] == 'road') // RTY=3
+      {
+        tags.highway = 'tertiary';
+      }
+      else if (tags['ref:road:type'] == 'street') // RTY=4
+      {
+        tags.highway = 'unclassified';
+      }
+      // Other should get picked up by the OTH field
+      else if (tags['ref:road:type'] == 'other')
+      {
+        tags.highway = 'road';
+      }
+    } // End if AP030
+
+
+    // ROR (Road Interchange Ramp)
+    if (tags.highway && tags.interchange_ramp == 'yes')
+    {
+      var roadList = ['motorway','trunk','primary','secondary','tertiary'];
+      if (roadList.indexOf(tags.highway) !== -1) tags.highway = tags.highway + '_link';
+    }
 
-            if (tObj.text && tObj.text !== '')
-            {
-                tags.note = tObj.text;
-            }
-            else
-            {
-                delete tags.note;
-            }
-        } // End process tags.note
+    // Add the LayerName to the source
+    if ((! tags.source) && layerName !== '') tags.source = 'ggdmv30:' + layerName.toLowerCase();
 
-        // Roads. GGDM30 is the same as TDSv61
-        if (attrs.F_CODE == 'AP030' || attrs.F_CODE == 'AQ075') // Road & Ice Road
-        {
-             // Set a Default: "It is a road but we don't know what it is"
-            tags.highway = 'road';
+    // If we have a UFI, store it
+    if (attrs.UFI)
+    {
+      tags.uuid = '{' + attrs['UFI'].toString().toLowerCase() + '}';
+    }
+    else
+    {
+      if (ggdm30.config.OgrAddUuid == 'true') tags.uuid = createUuid();
+    }
 
-            // Top level
-            if (tags['ref:road:type'] == 'motorway' || tags['ref:road:class'] == 'national_motorway')
-            {
-                tags.highway = 'motorway';
-            }
-            else if (tags['ref:road:type'] == 'limited_access_motorway')
-            {
-                tags.highway = 'trunk';
-            }
-            else if (tags['ref:road:class'] == 'primary')
-            {
-                tags.highway = 'primary';
-            }
-            else if (tags['ref:road:class'] == 'secondary')
-            {
-                tags.highway = 'secondary';
-            }
-            else if (tags['ref:road:class'] == 'local')
-            {
-                if (tags['ref:road:type'] == 'road') // RTY=3
-                {
-                    tags.highway = 'tertiary';
-                }
-                else
-                {
-                    tags.highway = 'unclassified';
-                }
-            }
-            else if (tags['ref:road:type'] == 'pedestrian')
-            {
-                tags.highway = 'pedestrian';
-            }
-            else if (tags['ref:road:type'] == 'road') // RTY=3
-            {
-                tags.highway = 'tertiary';
-            }
-            else if (tags['ref:road:type'] == 'street') // RTY=4
-            {
-                tags.highway = 'unclassified';
-            }
-            // Other should get picked up by the OTH field
-            else if (tags['ref:road:type'] == 'other')
-            {
-                tags.highway = 'road';
-            }
-        } // End if AP030
 
+    if (ggdm30.osmPostRules == undefined)
+    {
+      // ##############
+      // A "new" way of specifying rules. Jason came up with this while playing around with NodeJs
+      //
+      // Rules format:  ["test expression","output result"];
+      // Note: t = tags, a = attrs and attrs can only be on the RHS
+      var rulesList = [
+        ['t.barrier == \'dragons_teeth\' && !(t.tank_trap)','t.barrier = \'tank_trap\'; t.tank_trap = \'dragons_teeth\''],
+        ['t[\'bridge:movable\'] && t[\'bridge:movable\'] !== \'no\' && t[\'bridge:movable\'] !== \'unknown\'','t.bridge = \'movable\''],
+        ['t.navigationaid && !(t.aeroway)','t.aeroway = \'navigationaid\''],
+        ['t.amenity == \'stop\' && t[\'transport:type\'] == \'bus\'','t.highway = \'bus_stop\''],
+        ['t.diplomatic && !(t.amenity)','t.amenity = \'embassy\''],
+        ['t.boundary == \'protected_area\' && !(t.protect_class)','t.protect_class = \'4\''],
+        ['t.bunker_type && !(t.military)','t.military = \'bunker\''],
+        ['t.cable ==\'yes\' && t[\'cable:type\'] == \'power\'',' t.power = \'line\'; delete t.cable; delete t[\'cable:type\']'],
+        ['t.control_tower == \'yes\' && t.use == \'air_traffic_control\'','t[\'tower:type\'] = \'observation\''],
+        ['t.crossing == \'tank\'','t.highway = \'crossing\''],
+        ['t.desert_surface','t.surface = t.desert_surface; delete t.desert_surface'],
+        ['t.dock && !(t.waterway)','t.waterway = \'dock\''],
+        ['t.drive_in == \'yes\'','t.amenity = \'cinema\''],
+        ['t[\'generator:source\']','t.power = \'generator\''],
+        ['t[\'glacier:type\'] == \'icecap\' && t.natural == \'glacier\'','delete t.natural'],
+        ['t.golf == \'driving_range\' && !(t.leisure)','t.leisure = \'golf_course\''],
+        ['t.historic == \'castle\' && !(t.ruins) && !(t.building)','t.building = \'yes\''],
+        //["t.in_tunnel == 'yes' && !(t.tunnel)","t.tunnel = 'yes'; delete t.in_tunnel"],
+        ['t.industrial && !(t.landuse)','t.landuse = \'industrial\''],
+        ['(t.landuse == \'built_up_area\' || t.place == \'settlement\') && t.building','t[\'settlement:type\'] = t.building; delete t.building'],
+        ['t.leisure == \'stadium\'','t.building = \'yes\''],
+        ['t[\'material:vertical\']','t.material = t[\'material:vertical\']; delete t[\'material:vertical\']'],
+        ['t[\'monitoring:weather\'] == \'yes\'','t.man_made = \'monitoring_station\''],
+        ['t.natural ==\'spring\' && t[\'spring:type\'] == \'spring\'','delete t[\'spring:type\']'],
+        //["t.on_bridge == 'yes' && !(t.bridge)","t.bridge = 'yes'; delete t.on_bridge"],
+        ['t.product && t.man_made == \'storage_tank\'','t.content = t.product; delete t.product'],
+        ['t.public_transport == \'station\' && t[\'transport:type\'] == \'railway\'','t.railway = \'station\''],
+        ['t.public_transport == \'station\' && t[\'transport:type\'] == \'bus\'','t.bus = \'yes\''],
+        ['t.protect_class && !(t.boundary)','t.boundary = \'protected_area\''],
+        ['t.pylon ==\'yes\' && t[\'cable:type\'] == \'cableway\'',' t.aerialway = \'pylon\''],
+        ['t.pylon ==\'yes\' && t[\'cable:type\'] == \'power\'',' t.power = \'tower\''],
+        ['t[\'seamark:mooring:category\'] && !(t[\'seamark:type\'])','t[\'seamark:type\'] = \'mooring\''],
+        ['t.sidetrack && !(t.railway)','t.railway = \'rail\''],
+        ['t.sidetrack && !(t.service)','t.service = \'siding\''],
+        ['t.social_facility','t.amenity = \'social_facility\'; t[\'social_facility:for\'] = t.social_facility; t.social_facility = \'shelter\''],
+        ['t[\'theatre:type\'] && !(t.amenity)','t.amenity = \'theatre\''],
+        ['t[\'tower:material\']','t.material = t[\'tower:material\']; delete t[\'tower:material\']'],
+        ['t[\'tower:type\'] && !(t.man_made)','t.man_made = \'tower\''],
+        ['t.use == \'islamic_prayer_hall\' && !(t.amenity)','t.amenity = \'place_of_worship\''],
+        ['t.water || t.landuse == \'basin\'','t.natural = \'water\''],
+        ['t.waterway == \'flow_control\'','t.flow_control = \'sluice_gate\''],
+        ['t.waterway == \'vanishing_point\' && t[\'water:sink:type\'] == \'sinkhole\'','t.natural = \'sinkhole\'; delete t.waterway; delete t[\'water:sink:type\']'],
+        ['t.wetland && !(t.natural)','t.natural = \'wetland\''],
+        ['t[\'width:minimum_traveled_way\'] && !(t.width)','t.width = t[\'width:minimum_traveled_way\']']
+      ];
+
+      ggdm30.osmPostRules = translate.buildComplexRules(rulesList);
+    }
+
+    //translate.applyComplexRules(tags,attrs,ggdm30.osmPostRules);
+    // Pulling this out of translate
+    for (var i = 0, rLen = ggdm30.osmPostRules.length; i < rLen; i++)
+    {
+      if (ggdm30.osmPostRules[i][0](tags)) ggdm30.osmPostRules[i][1](tags,attrs);
+    }
 
-        // ROR (Road Interchange Ramp)
-        if (tags.highway && tags.interchange_ramp == 'yes')
-        {
-            var roadList = ['motorway','trunk','primary','secondary','tertiary'];
-            if (roadList.indexOf(tags.highway) !== -1) tags.highway = tags.highway + '_link';
-        }
+    // ##############
 
-        // Add the LayerName to the source
-        if ((! tags.source) && layerName !== '') tags.source = 'ggdmv30:' + layerName.toLowerCase();
+    // Road & Railway Crossings
+    // Road/Rail = crossing
+    // Road + Rail = level_crossing
+    if (tags.crossing_point)
+    {
+      if (tags['transport:type'] == 'railway')
+      {
+        tags.railway = 'crossing';
 
-        // If we have a UFI, store it
-        if (attrs.UFI)
+        if (tags['transport:type:2'] == 'road') tags.railway = 'level_crossing';
+      }
+      else if (tags['transport:type'] == 'road')
+      {
+        if (tags['transport:type:2'] == 'railway')
         {
-            tags.uuid = '{' + attrs['UFI'].toString().toLowerCase() + '}';
+          tags.railway = 'level_crossing';
         }
         else
         {
-            if (ggdm30.config.OgrAddUuid == 'true') tags.uuid = createUuid();
+          tags.highway = 'crossing';
         }
+      }
+    } // End crossing_point
 
 
-        if (ggdm30.osmPostRules == undefined)
-        {
-            // ##############
-            // A "new" way of specifying rules. Jason came up with this while playing around with NodeJs
-            //
-            // Rules format:  ["test expression","output result"];
-            // Note: t = tags, a = attrs and attrs can only be on the RHS
-            var rulesList = [
-            ["t.barrier == 'dragons_teeth' && !(t.tank_trap)","t.barrier = 'tank_trap'; t.tank_trap = 'dragons_teeth'"],
-            ["t['bridge:movable'] && t['bridge:movable'] !== 'no' && t['bridge:movable'] !== 'unknown'","t.bridge = 'movable'"],
-            ["t.navigationaid && !(t.aeroway)","t.aeroway = 'navigationaid'"],
-            ["t.amenity == 'stop' && t['transport:type'] == 'bus'","t.highway = 'bus_stop'"],
-            ["t.diplomatic && !(t.amenity)","t.amenity = 'embassy'"],
-            ["t.boundary == 'protected_area' && !(t.protect_class)","t.protect_class = '4'"],
-            ["t.bunker_type && !(t.military)","t.military = 'bunker'"],
-            ["t.cable =='yes' && t['cable:type'] == 'power'"," t.power = 'line'; delete t.cable; delete t['cable:type']"],
-            ["t.control_tower == 'yes' && t.use == 'air_traffic_control'","t['tower:type'] = 'observation'"],
-            ["t.crossing == 'tank'","t.highway = 'crossing'"],
-            ["t.desert_surface","t.surface = t.desert_surface; delete t.desert_surface"],
-            ["t.dock && !(t.waterway)","t.waterway = 'dock'"],
-            ["t.drive_in == 'yes'","t.amenity = 'cinema'"],
-            ["t['generator:source']","t.power = 'generator'"],
-            ["t['glacier:type'] == 'icecap' && t.natural == 'glacier'","delete t.natural"],
-            ["t.golf == 'driving_range' && !(t.leisure)","t.leisure = 'golf_course'"],
-            ["t.historic == 'castle' && !(t.ruins) && !(t.building)","t.building = 'yes'"],
-            //["t.in_tunnel == 'yes' && !(t.tunnel)","t.tunnel = 'yes'; delete t.in_tunnel"],
-            ["t.industrial && !(t.landuse)","t.landuse = 'industrial'"],
-            ["(t.landuse == 'built_up_area' || t.place == 'settlement') && t.building","t['settlement:type'] = t.building; delete t.building"],
-            ["t.leisure == 'stadium'","t.building = 'yes'"],
-            ["t['material:vertical']","t.material = t['material:vertical']; delete t['material:vertical']"],
-            ["t['monitoring:weather'] == 'yes'","t.man_made = 'monitoring_station'"],
-            ["t.natural =='spring' && t['spring:type'] == 'spring'","delete t['spring:type']"],
-            //["t.on_bridge == 'yes' && !(t.bridge)","t.bridge = 'yes'; delete t.on_bridge"],
-            ["t.product && t.man_made == 'storage_tank'","t.content = t.product; delete t.product"],
-            ["t.public_transport == 'station' && t['transport:type'] == 'railway'","t.railway = 'station'"],
-            ["t.public_transport == 'station' && t['transport:type'] == 'bus'","t.bus = 'yes'"],
-            ["t.protect_class && !(t.boundary)","t.boundary = 'protected_area'"],
-            ["t.pylon =='yes' && t['cable:type'] == 'cableway'"," t.aerialway = 'pylon'"],
-            ["t.pylon =='yes' && t['cable:type'] == 'power'"," t.power = 'tower'"],
-            ["t['seamark:mooring:category'] && !(t['seamark:type'])","t['seamark:type'] = 'mooring'"],
-            ["t.sidetrack && !(t.railway)","t.railway = 'rail'"],
-            ["t.sidetrack && !(t.service)","t.service = 'siding'"],
-            ["t.social_facility","t.amenity = 'social_facility'; t['social_facility:for'] = t.social_facility; t.social_facility = 'shelter'"],
-            ["t['theatre:type'] && !(t.amenity)","t.amenity = 'theatre'"],
-            ["t['tower:material']","t.material = t['tower:material']; delete t['tower:material']"],
-            ["t['tower:type'] && !(t.man_made)","t.man_made = 'tower'"],
-            ["t.use == 'islamic_prayer_hall' && !(t.amenity)","t.amenity = 'place_of_worship'"],
-            ["t.water || t.landuse == 'basin'","t.natural = 'water'"],
-            ["t.waterway == 'flow_control'","t.flow_control = 'sluice_gate'"],
-            ["t.waterway == 'vanishing_point' && t['water:sink:type'] == 'sinkhole'","t.natural = 'sinkhole'; delete t.waterway; delete t['water:sink:type']"],
-            ["t.wetland && !(t.natural)","t.natural = 'wetland'"],
-            ["t['width:minimum_traveled_way'] && !(t.width)","t.width = t['width:minimum_traveled_way']"]
-            ];
-
-            ggdm30.osmPostRules = translate.buildComplexRules(rulesList);
-        }
-
-        //translate.applyComplexRules(tags,attrs,ggdm30.osmPostRules);
-        // Pulling this out of translate
-        for (var i = 0, rLen = ggdm30.osmPostRules.length; i < rLen; i++)
-        {
-            if (ggdm30.osmPostRules[i][0](tags)) ggdm30.osmPostRules[i][1](tags,attrs);
-        }
+    // Add a building tag to Buildings and Fortified Buildings if we don't have one
+    // We can't do this in the funky rules function as it uses "attrs" _and_ "tags"
+    if ((attrs.F_CODE == 'AL013' || attrs.F_CODE == 'AH055') && !(tags.building))
+    {
+      tags.building = 'yes';
+    }
 
-        // ##############
 
-        // Road & Railway Crossings
-        // Road/Rail = crossing
-        // Road + Rail = level_crossing
-        if (tags.crossing_point)
-        {
-            if (tags['transport:type'] == 'railway')
-            {
-                tags.railway = 'crossing';
+    // Fix the building 'use' tag. If the building has a 'use' and no specific building tag. Give it one
+    if (tags.building == 'yes' && tags.use)
+    {
+      if ((tags.use.indexOf('manufacturing') > -1) || (tags.use.indexOf('processing') > -1))
+      {
+        tags.building = 'industrial';
+      }
+      // NOTE: Shops have been rempved from "use"
+      // Sort out shops
+      // else if (hoot.OsmSchema.getTagVertex("shop=" + tags.use).name != "shop=*")
+      // {
+      //    hoot.warn(hoot.OsmSchema.getTagVertex("shop=" + tags.use));
+      //    hoot.warn(hoot.OsmSchema.isAncestor("shop=" + tags.use, "shop"));
+      //    hoot.warn(tags.use);
+      //    tags.shop = tags.use;
+      //    delete tags.use;
+      // }
+
+    } // End building & use tags
+
+    // Education:
+    if (tags['isced:level'] || tags.use == 'education')
+    {
+      if (tags.building == 'yes')
+      {
+        tags.building = 'school';
+      }
+      else if (tags.facility)
+      {
+        tags.amenity = 'school';
+      }
+    }
+
+    if (tags.use == 'vocational_education')
+    {
+      if (tags.building == 'yes')
+      {
+        tags.building = 'college';
+      }
+      else if (tags.facility)
+      {
+        tags.amenity = 'college';
+      }
+    }
 
-                if (tags['transport:type:2'] == 'road') tags.railway = 'level_crossing';
-            }
-            else if (tags['transport:type'] == 'road')
-            {
-                if (tags['transport:type:2'] == 'railway')
-                {
-                    tags.railway = 'level_crossing';
-                }
-                else
-                {
-                    tags.highway = 'crossing';
-                }
-            }
-        } // End crossing_point
 
 
-        // Add a building tag to Buildings and Fortified Buildings if we don't have one
-        // We can't do this in the funky rules function as it uses "attrs" _and_ "tags"
-        if ((attrs.F_CODE == 'AL013' || attrs.F_CODE == 'AH055') && !(tags.building))
-        {
-            tags.building = 'yes';
-        }
+    // A facility is an area. Therefore "use" becomes "amenity". "Building" becomes "landuse"
+    if (tags.facility && tags.use)
+    {
+      if ((tags.use.indexOf('manufacturing') > -1) || (tags.use.indexOf('processing') > -1))
+      {
+        tags.man_made = 'works';
+      }
+    }
+    // Add defaults for common features
+    if (attrs.F_CODE == 'AP020' && !(tags.junction)) tags.junction = 'yes';
+    if (attrs.F_CODE == 'AQ040' && !(tags.bridge)) tags.bridge = 'yes';
+    if (attrs.F_CODE == 'BH140' && !(tags.waterway)) tags.waterway = 'river';
+
+    // Fix lifecycle tags
+    switch (tags.condition)
+    {
+    case undefined: // Break early if no value
+      break;
+
+    case 'construction':
+      for (var typ of ['highway','bridge','railway','building'])
+      {
+        if (tags[typ])
+        {
+          tags.construction = tags[typ];
+          tags[typ] = 'construction';
+          delete tags.condition;
+          break;
+        }
+      }
+      break;
+
+    case 'abandoned':
+      for (var typ of ['highway','bridge','railway','building'])
+      {
+        if (tags[typ])
+        {
+          tags['abandoned:' + typ] = tags[typ];
+          delete tags[typ];
+          delete tags.condition;
+          break;
+        }
+      }
+      break;
+
+    case 'dismantled':
+      for (var typ of ['highway','bridge','railway','building'])
+      {
+        if (tags[typ])
+        {
+          tags['demolished:' + typ] = tags[typ];
+          delete tags[typ];
+          delete tags.condition;
+          break;
+        }
+      }
+      break;
+    } // End switch condifion
+
+    // Not sure about adding a Highway tag to this
+    // if (attrs.F_CODE == 'AQ040' && !(tags.highway)) tags.highway = 'yes';
+
+    // Denominations without religions - from ZI037_REL which has some denominations as religions
+    if (tags.denomination)
+    {
+      switch (tags.denomination)
+      {
+      case 'roman_catholic':
+      case 'orthodox':
+      case 'protestant':
+      case 'chaldean_catholic':
+      case 'nestorian': // Not sure about this
+        tags.religion = 'christian';
+        break;
+
+      case 'shia':
+      case 'sunni':
+        tags.religion = 'muslim';
+        break;
+      } // End switch
+    }
+
+    // Religious buildings: Church, Pagoda, Temple etc
+    if (attrs.ZI037_REL && tags.amenity !== 'place_of_worship')
+    {
+      tags.amenity = 'place_of_worship';
+    }
+
+    // Fords and Roads
+    // Putting this on hold for a while
+    // if (attrs.F_CODE == 'BH070' && !(tags.highway)) tags.highway = 'road';
+    // if ('ford' in tags && !(tags.highway)) tags.highway = 'road';
+
+    // AK030 - Amusement Parks
+    // F_CODE translation == tourism but FFN translation could be leisure
+    // E.g. water parks
+    if (attrs.F_CODE == 'AK030')
+    {
+      if (tags.leisure && tags.tourism)
+      {
+        delete tags.tourism;
+      }
+    }
 
+    // Fix the ZI020_GE4X Values
+    var ge4meta = ['is_in:country_code','country_code:second','country_code:third','country_code:fourth',
+      'country_code:first_side','country_code:second_side'];
 
-        // Fix the building 'use' tag. If the building has a 'use' and no specific building tag. Give it one
-        if (tags.building == 'yes' && tags.use)
-        {
-            if ((tags.use.indexOf('manufacturing') > -1) || (tags.use.indexOf('processing') > -1))
-            {
-                tags.building = 'industrial';
-            }
-            // NOTE: Shops have been rempved from "use"
-            // Sort out shops
-            // else if (hoot.OsmSchema.getTagVertex("shop=" + tags.use).name != "shop=*")
-            // {
-            //    hoot.warn(hoot.OsmSchema.getTagVertex("shop=" + tags.use));
-            //    hoot.warn(hoot.OsmSchema.isAncestor("shop=" + tags.use, "shop"));
-            //    hoot.warn(tags.use);
-            //    tags.shop = tags.use;
-            //    delete tags.use;
-            // }
-
-        } // End building & use tags
-
-        // Education:
-        if (tags['isced:level'] || tags.use == 'education')
+    for (var i=0, iLen=ge4meta.length; i < iLen; i++)
+    {
+      if (tags[ge4meta[i]])
+      {
+        if (ggdm30.rules.ge4List[tags[ge4meta[i]]])
         {
-            if (tags.building == 'yes')
-            {
-                tags.building = 'school'
-            }
-            else if (tags.facility)
-            {
-                tags.amenity = 'school';
-            }
+          tags[ge4meta[i]] = ggdm30.rules.ge4List[tags[ge4meta[i]]];
         }
-
-        if (tags.use == 'vocational_education')
+        else
         {
-            if (tags.building == 'yes')
-            {
-                tags.building = 'college'
-            }
-            else if (tags.facility)
-            {
-                tags.amenity = 'college';
-            }
+          hoot.logWarn('Dropping invalid ' + ge4meta[i] + ' value: ' + tags[ge4meta[i]]);
+          delete tags[ge4meta[i]];
         }
+      }
+    } // End for GE4 loop
 
+  }, // End of applyToOsmPostProcessing
 
+  // ##### End of the xxToOsmxx Block #####
 
-        // A facility is an area. Therefore "use" becomes "amenity". "Building" becomes "landuse"
-        if (tags.facility && tags.use)
-        {
-            if ((tags.use.indexOf('manufacturing') > -1) || (tags.use.indexOf('processing') > -1))
-            {
-                tags.man_made = 'works';
-            }
-        }
-        // Add defaults for common features
-        if (attrs.F_CODE == 'AP020' && !(tags.junction)) tags.junction = 'yes';
-        if (attrs.F_CODE == 'AQ040' && !(tags.bridge)) tags.bridge = 'yes';
-        if (attrs.F_CODE == 'BH140' && !(tags.waterway)) tags.waterway = 'river';
+  // #####################################################################################################
 
-        // Fix lifecycle tags
-        switch (tags.condition)
-        {
-            case undefined: // Break early if no value
-                break;
+  // ##### Start of the xxToOgrxx Block #####
 
-            case 'construction':
-                for (var typ of ['highway','bridge','railway','building'])
-                {
-                    if (tags[typ])
-                    {
-                        tags.construction = tags[typ];
-                        tags[typ] = 'construction';
-                        delete tags.condition;
-                        break;
-                    }
-                }
-                break;
+  applyToOgrPreProcessing: function(tags, attrs, geometryType)
+  {
+    // Remove Hoot assigned tags for the source of the data
+    if (tags['source:ingest:datetime']) delete tags['source:ingest:datetime'];
+    if (tags.area) delete tags.area;
+    if (tags['error:circular']) delete tags['error:circular'];
+    if (tags['hoot:status']) delete tags['hoot:status'];
 
-            case 'abandoned':
-                for (var typ of ['highway','bridge','railway','building'])
-                {
-                    if (tags[typ])
-                    {
-                        tags['abandoned:' + typ] = tags[typ];
-                        delete tags[typ];
-                        delete tags.condition;
-                        break;
-                    }
-                }
-                break;
+    // Initial cleanup
+    for (var i in tags)
+    {
+      // Remove empty tags
+      if (tags[i] == '')
+      {
+        delete tags[i];
+        continue;
+      }
+
+      // Convert "abandoned:XXX" and "disused:XXX"features
+      if ((i.indexOf('abandoned:') !== -1) || (i.indexOf('disused:') !== -1))
+      {
+        // Hopeing there is only one ':' in the tag name...
+        var tList = i.split(':');
+        tags[tList[1]] = tags[i];
+        tags.condition = 'abandoned';
+        delete tags[i];
+        continue;
+      }
+
+      // Convert "demolished:XXX" features
+      if (i.indexOf('demolished:') !== -1)
+      {
+        // Hopeing there is only one ':' in the tag name...
+        var tList = i.split(':');
+        tags[tList[1]] = tags[i];
+        tags.condition = 'dismantled';
+        delete tags[i];
+        continue;
+      }
+    } // End Cleanup loop
+
+    // Lifecycle: This is a bit funky and should probably be done with a fancy function instead of
+    // repeating the code
+    switch (tags.highway)
+    {
+    case undefined: // Break early if no value
+      break;
+
+    case 'abandoned':
+    case 'disused':
+      tags.highway = 'road';
+      tags.condition = 'abandoned';
+      break;
+
+    case 'demolished':
+      tags.highway = 'road';
+      tags.condition = 'dismantled';
+      break;
+
+    case 'construction':
+      if (tags.construction)
+      {
+        tags.highway = tags.construction;
+        delete tags.construction;
+      }
+      else
+      {
+        tags.highway = 'road';
+      }
+      tags.condition = 'construction';
+      break;
+    }
+
+    switch (tags.railway)
+    {
+    case undefined: // Break early if no value
+      break;
+
+    case 'abandoned':
+    case 'disused':
+      tags.railway = 'rail';
+      tags.condition = 'abandoned';
+      break;
+
+    case 'demolished':
+      tags.railway = 'yes';
+      tags.condition = 'dismantled';
+      break;
+
+    case 'construction':
+      if (tags.construction)
+      {
+        tags.railway = tags.construction;
+        delete tags.construction;
+      }
+      else
+      {
+        tags.railway = 'rail';
+      }
+      tags.condition = 'construction';
+      break;
+    }
+
+    switch (tags.building)
+    {
+    case undefined: // Break early if no value
+      break;
+
+    case 'abandoned':
+    case 'disused':
+      tags.building = 'yes';
+      tags.condition = 'abandoned';
+      break;
+
+    case 'destroyed':
+      tags.building = 'yes';
+      tags.condition = 'destroyed';
+      break;
+
+    case 'demolished':
+      tags.building = 'yes';
+      tags.condition = 'dismantled';
+      break;
+
+    case 'construction':
+      if (tags.construction)
+      {
+        tags.building = tags.construction;
+        delete tags.construction;
+      }
+      else
+      {
+        tags.building = 'yes';
+      }
+      tags.condition = 'construction';
+      break;
+    }
+
+    switch (tags.bridge)
+    {
+    case undefined: // Break early if no value
+      break;
+
+    case 'abandoned':
+    case 'disused':
+      tags.bridge = 'yes';
+      tags.condition = 'abandoned';
+      break;
+
+    case 'destroyed':
+      tags.bridge = 'yes';
+      tags.condition = 'destroyed';
+      break;
+
+    case 'demolished':
+      tags.bridge = 'yes';
+      tags.condition = 'dismantled';
+      break;
+
+    case 'construction':
+      if (tags.construction)
+      {
+        tags.bridge = tags.construction;
+        delete tags.construction;
+      }
+      else
+      {
+        tags.bridge = 'yes';
+      }
+      tags.condition = 'construction';
+      break;
+    }
+
+
+    if (ggdm30.preRules == undefined)
+    {
+      // See ToOsmPostProcessing for more details about rulesList
+      var rulesList = [
+        ['t.aeroway == \'navigationaid\' && t.navigationaid','delete t.navigationaid'],
+        ['t.amenity == \'bus_station\'','t.public_transport = \'station\'; t[\'transport:type\'] = \'bus\''],
+        // ["t.amenity == 'marketplace'","t.facility = 'yes'"],
+        ['t.barrier == \'tank_trap\' && t.tank_trap == \'dragons_teeth\'','t.barrier = \'dragons_teeth\'; delete t.tank_trap'],
+        ['t.communication == \'line\'','t[\'cable:type\'] = \'communication\''],
+        ['t.content && !(t.product)','t.product = t.content; delete t.content'],
+        ['t.control_tower && t.man_made == \'tower\'','delete t.man_made'],
+        ['t.crossing == \'tank\' && t.highway == \'crossing\'','delete t.highway'],
+        ['t.diplomatic && t.amenity == \'embassy\'','delete t.amenity'],
+        ['t.dock && t.waterway == \'dock\'','delete t.waterway'],
+        ['t.golf == \'driving_range\' && t.leisure == \'golf_course\'','delete t.leisure'],
+        ['t.highway == \'bus_stop\'','t[\'transport:type\'] = \'bus\''],
+        ['t.highway == \'crossing\'','t[\'transport:type\'] = \'road\';a.F_CODE = \'AQ062\'; delete t.highway'],
+        ['t.highway == \'give-way\'','a.F_CODE = \'AQ062\''],
+        ['t.highway == \'mini_roundabout\'','t.junction = \'roundabout\''],
+        ['t.highway == \'steps\'','t.highway = \'footway\''],
+        ['t.highway == \'stop\'','a.F_CODE = \'AQ062\''],
+        ['t.historic == \'castle\' && t.building','delete t.building'],
+        ['t.historic == \'castle\' && t.ruins == \'yes\'','t.condition = \'destroyed\'; delete t.ruins'],
+        ['t.landcover == \'snowfield\' || t.landcover == \'ice-field\'','a.F_CODE = \'BJ100\''],
+        ['t.landuse == \'allotments\'','t.landuse = \'farmland\''],
+        ['t.landuse == \'brownfield\'','t.landuse = \'built_up_area\'; t.condition = \'destroyed\''],
+        ['t.landuse == \'construction\'','t.landuse = \'built_up_area\'; t.condition = \'construction\''],
+        ['t.landuse == \'farm\'','t.landuse = \'farmland\''],
+        ['t.landuse == \'farmland\' && t.crop == \'fruit_tree\'','t.landuse = \'orchard\''],
+        ['t.landuse == \'farmyard\'','t.facility = \'yes\'; t.use = \'agriculture\'; delete t.landuse'],
+        ['t.landuse == \'grass\'','t.natural = \'grassland\'; t[\'grassland:type\'] = \'grassland\';'],
+        ['t.landuse == \'meadow\'','t.natural = \'grassland\'; t[\'grassland:type\'] = \'meadow\';'],
+        ['t.landuse == \'military\'','t.military = \'installation\'; delete t.landuse'],
+        ['t.landuse == \'railway\' && t[\'railway:yard\'] == \'marshalling_yard\'','a.F_CODE = \'AN060\''],
+        ['t.leisure == \'recreation_ground\'','t.landuse = \'recreation_ground\'; delete t.leisure'],
+        ['t.landuse == \'reservoir\'','t.water = \'reservoir\'; delete t.landuse'],
+        ['t.landuse == \'retail\'','t.landuse = \'built_up_area\'; t.use = \'commercial\''],
+        ['t.landuse == \'scrub\'','t.natural = \'scrub\'; delete t.landuse'],
+        ['t.launch_pad','delete t.launch_pad; t.aeroway=\'launchpad\''],
+        ['t.leisure == \'sports_centre\'','t.facility = \'yes\'; t.use = \'recreation\'; delete t.leisure'],
+        ['t.leisure == \'stadium\' && t.building','delete t.building'],
+        ['t.man_made && t.building == \'yes\'','delete t.building'],
+        ['t.man_made == \'embankment\'','t.embankment = \'yes\'; delete t.man_made'],
+        ['t.man_made == \'launch_pad\'','delete t.man_made; t.aeroway=\'launchpad\''],
+        ['t.median == \'yes\'','t.is_divided = \'yes\''],
+        ['t.military == \'barracks\'','t.use = \'dormitory\''],
+        ['t.natural == \'desert\' && t.surface','t.desert_surface = t.surface; delete t.surface'],
+        ['t.natural == \'sinkhole\'','a.F_CODE = \'BH145\'; t[\'water:sink:type\'] = \'sinkhole\'; delete t.natural'],
+        ['t.natural == \'spring\' && !(t[\'spring:type\'])','t[\'spring:type\'] = \'spring\''],
+        ['t.natural == \'wood\'','t.landuse = \'forest\'; delete t.natural'],
+        ['t.power == \'pole\'','t[\'cable:type\'] = \'power\'; t[\'tower:shape\'] = \'pole\''],
+        ['t.power == \'tower\'','t[\'cable:type\'] = \'power\''],
+        ['t.power == \'line\'','t[\'cable:type\'] = \'power\'; t.cable = \'yes\'; delete t.power'],
+        ['t.power == \'generator\'','t.use = \'power_generation\'; a.F_CODE = \'AL013\''],
+        ['t.rapids == \'yes\'','t.waterway = \'rapids\'; delete t.rapids'],
+        ['t.railway == \'station\'','t.public_transport = \'station\';  t[\'transport:type\'] = \'railway\''],
+        ['t.railway == \'level_crossing\'','t[\'transport:type\'] = \'railway\';t[\'transport:type2\'] = \'road\'; a.F_CODE = \'AQ062\'; delete t.railway'],
+        ['t.railway == \'crossing\'','t[\'transport:type\'] = \'railway\'; a.F_CODE = \'AQ062\'; delete t.railway'],
+        ['t.resource','t.raw_material = t.resource; delete t.resource'],
+        ['t.route == \'road\' && !(t.highway)','t.highway = \'road\'; delete t.route'],
+        // ["(t.shop || t.office) &&  !(t.building)","a.F_CODE = 'AL013'"],
+        ['t.social_facility == \'shelter\'','t.social_facility = t[\'social_facility:for\']; delete t.amenity; delete t[\'social_facility:for\']'],
+        ['t[\'tower:type\'] == \'minaret\' && t.man_made == \'tower\'','delete t.man_made'],
+        ['t.tunnel == \'building_passage\'','t.tunnel = \'yes\''],
+        ['t.use == \'islamic_prayer_hall\' && t.amenity == \'place_of_worship\'','delete t.amenity'],
+        ['!(t.water) && t.natural == \'water\'','t.water = \'lake\''],
+        ['t.wetland && t.natural == \'wetland\'','delete t.natural'],
+        ['t.water == \'river\'','t.waterway = \'river\''],
+        ['t.waterway == \'riverbank\'','t.waterway = \'river\'']
+      ];
+
+      ggdm30.tdsPreRules = translate.buildComplexRules(rulesList);
+    }
+
+    // Apply the rulesList
+    //translate.applyComplexRules(tags,attrs,ggdm30.tdsPreRules);
+    // Pulling this out of translate
+    for (var i = 0, rLen = ggdm30.tdsPreRules.length; i < rLen; i++)
+    {
+      if (ggdm30.tdsPreRules[i][0](tags)) ggdm30.tdsPreRules[i][1](tags,attrs);
+    }
 
-            case 'dismantled':
-                for (var typ of ['highway','bridge','railway','building'])
-                {
-                    if (tags[typ])
-                    {
-                        tags['demolished:' + typ] = tags[typ];
-                        delete tags[typ];
-                        delete tags.condition;
-                    break;
-                    }
-                }
-                break;
-        } // End switch condifion
+    // Fix up OSM 'walls' around facilities
+    if ((tags.barrier == 'wall' || tags.barrier == 'fence') && geometryType == 'Area')
+    {
+      if (tags.landuse == 'military' || tags.military)
+      {
+        attrs.F_CODE = 'SU001'; // Military Installation
+      }
+      else
+      {
+        attrs.F_CODE = 'AL010'; // Facility
+      }
+
+      delete tags.barrier; // Take away the walls...
+    }
+
+
+    // going out on a limb and processing OSM specific tags:
+    // - Building == a thing,
+    // - Amenity == The area around a thing
+    // Note: amenity=place_of_worship is a special case. It _should_ have an associated building tag
+    var facilityList = {
+      'school':'850','university':'855','college':'857','hospital':'860'
+    };
+
+    if (tags.amenity in facilityList)
+    {
+      if (geometryType == 'Area')
+      {
+        attrs.F_CODE = 'AL010'; // Facility
+      }
+      else
+      {
+        // Make sure we don't turn point facilities into buildings
+        if (!(tags.facility = 'yes'))
+        {
+          // Debug
+          // print('Making a building: ' + tags.facility);
+          attrs.F_CODE = 'AL013'; // Building
+        }
+      }
+
+      // If we don't have a Feature Function then assign one
+      if (!(attrs.FFN))
+      {
+        // attrs.FFN = facilityList[tags.amenity];
+        // Debug
+        // print('PreDropped: amenity = ' + tags.amenity);
+        delete tags.amenity;
+      }
+    }
+
+    // Adding a custom rule for malls to override the rules above
+    // All of the other "shops" are buildings
+    if (tags.shop == 'mall') attrs.F_CODE = 'AG030';
 
-        // Not sure about adding a Highway tag to this
-        // if (attrs.F_CODE == 'AQ040' && !(tags.highway)) tags.highway = 'yes';
+    // Churches etc
+    if (tags.building && ! tags.amenity)
+    {
+      var how = [ 'church','chapel','cathedral','mosque','pagoda','shrine','temple',
+        'synagogue','tabernacle','stupa'];
+      if (how.indexOf(tags.building) > -1)
+      {
+        tags.amenity = 'place_of_worship';
+      }
+
+      var rc = [ 'mission','religious_community','seminary','convent','monastry',
+        'noviciate','hermitage','retrest','marabout'];
+      if (rc.indexOf(tags.building) > -1)
+      {
+        tags.use = 'religious_activities';
+      }
+    }
+
+    // Fix up water features from OSM
+    if (tags.natural == 'water' && !(tags.water))
+    {
+      if (geometryType =='Line')
+      {
+        tags.waterway = 'river';
+        attrs.F_CODE = 'BH140';
+      }
+      else
+      {
+        tags.water = 'lake';
+        attrs.F_CODE = 'BH082';
+      }
+    }
+
+    // Cutlines/Cleared Ways & Highways
+    // This might need a cleanup
+    if (tags.man_made == 'cutline' && tags.highway)
+    {
+      if (geometryType == 'Area')
+      {
+        // Keep the cutline/cleared way
+        attrs.F_CODE = 'EC040';
 
-        // Denominations without religions - from ZI037_REL which has some denominations as religions
-        if (tags.denomination)
+        if (tags.memo)
         {
-            switch (tags.denomination)
-            {
-                case 'roman_catholic':
-                case 'orthodox':
-                case 'protestant':
-                case 'chaldean_catholic':
-                case 'nestorian': // Not sure about this
-                    tags.religion = 'christian';
-                    break;
-
-                case 'shia':
-                case 'sunni':
-                    tags.religion = 'muslim';
-                    break;
-            } // End switch
+          tags.memo = tags.memo + ';highway:' + tags.highway;
         }
-
-        // Religious buildings: Church, Pagoda, Temple etc
-        if (attrs.ZI037_REL && tags.amenity !== 'place_of_worship')
+        else
         {
-            tags.amenity = 'place_of_worship';
+          tags.memo = 'highway:' + tags.highway;
         }
 
-        // Fords and Roads
-        // Putting this on hold for a while
-        // if (attrs.F_CODE == 'BH070' && !(tags.highway)) tags.highway = 'road';
-        // if ('ford' in tags && !(tags.highway)) tags.highway = 'road';
+        delete tags.highway;
+      }
+      else
+      {
+        // Drop the cutline/cleared way
+        delete tags.man_made;
 
-        // AK030 - Amusement Parks
-        // F_CODE translation == tourism but FFN translation could be leisure
-        // E.g. water parks
-        if (attrs.F_CODE == 'AK030')
+        if (tags.memo)
         {
-            if (tags.leisure && tags.tourism)
-            {
-                delete tags.tourism;
-            }
+          tags.memo = tags.memo + ';cleared_way:yes';
         }
+        else
+        {
+          tags.memo = 'cleared_way:yes';
+        }
+      }
+    }
 
-        // Fix the ZI020_GE4X Values
-        var ge4meta = ['is_in:country_code','country_code:second','country_code:third','country_code:fourth',
-                        'country_code:first_side','country_code:second_side'];
+    // Places, localities and regions
+    if (tags.place)
+    {
+      switch (tags.place)
+      {
+      case 'city':
+      case 'town':
+      case 'suburb':
+      case 'neighbourhood':
+      case 'quarter':
+      case 'village':
+      case 'hamlet':
+      case 'yes':  // We set this as a default when going to OSM
+        attrs.F_CODE = 'AL020'; // Built Up Area
+        delete tags.place;
+        break;
+
+      case 'isolated_dwelling':
+        attrs.F_CODE = 'AL105'; // Settlement
+        delete tags.place;
+        break;
+
+      case 'populated':
+      case 'state':
+      case 'county':
+      case 'region':
+      case 'locality':
+      case 'municipality':
+      case 'borough':
+      case 'unknown':
+        attrs.F_CODE = 'ZD045'; // Annotated Location
+        if (tags.memo)
+        {
+          tags.memo = tags.memo + ';annotation:' + tags.place;
+        }
+        else
+        {
+          tags.memo = 'annotation:' + tags.place;
+        }
+        delete tags.place;
+        break;
 
-        for (var i=0, iLen=ge4meta.length; i < iLen; i++)
+      case 'island':
+      case 'islet':
+        // If we have a coastline around an Island, decide if we are going make an Island
+        // or a Coastline
+        if (tags.natural == 'coastline')
         {
-            if (tags[ge4meta[i]])
-            {
-                if (ggdm30.rules.ge4List[tags[ge4meta[i]]])
-                {
-                    tags[ge4meta[i]] = ggdm30.rules.ge4List[tags[ge4meta[i]]];
-                }
-                else
-                {
-                    hoot.logWarn('Dropping invalid ' + ge4meta[i] + ' value: ' + tags[ge4meta[i]]);
-                    delete tags[ge4meta[i]];
-                }
-            }
-        } // End for GE4 loop
-
-    }, // End of applyToOsmPostProcessing
-
-    // ##### End of the xxToOsmxx Block #####
-
-// #####################################################################################################
-
-    // ##### Start of the xxToOgrxx Block #####
-
-    applyToOgrPreProcessing: function(tags, attrs, geometryType)
-    {
-        // Remove Hoot assigned tags for the source of the data
-        if (tags['source:ingest:datetime']) delete tags['source:ingest:datetime'];
-        if (tags.area) delete tags.area;
-        if (tags['error:circular']) delete tags['error:circular'];
-        if (tags['hoot:status']) delete tags['hoot:status'];
-
-        // Initial cleanup
-        for (var i in tags)
-        {
-            // Remove empty tags
-            if (tags[i] == '')
-            {
-                delete tags[i];
-                continue;
-            }
-
-            // Convert "abandoned:XXX" and "disused:XXX"features
-            if ((i.indexOf('abandoned:') !== -1) || (i.indexOf('disused:') !== -1))
-            {
-                // Hopeing there is only one ':' in the tag name...
-                var tList = i.split(':');
-                tags[tList[1]] = tags[i];
-                tags.condition = 'abandoned';
-                delete tags[i];
-                continue;
-            }
-
-            // Convert "demolished:XXX" features
-            if (i.indexOf('demolished:') !== -1)
-            {
-                // Hopeing there is only one ':' in the tag name...
-                var tList = i.split(':');
-                tags[tList[1]] = tags[i];
-                tags.condition = 'dismantled';
-                delete tags[i];
-                continue;
-            }
-        } // End Cleanup loop
-
-        // Lifecycle: This is a bit funky and should probably be done with a fancy function instead of
-        // repeating the code
-        switch (tags.highway)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'abandoned':
-            case 'disused':
-                tags.highway = 'road';
-                tags.condition = 'abandoned';
-                break;
-
-            case 'demolished':
-                tags.highway = 'road';
-                tags.condition = 'dismantled';
-                break;
-
-            case 'construction':
-                if (tags.construction)
-                {
-                    tags.highway = tags.construction;
-                    delete tags.construction;
-                }
-                else
-                {
-                    tags.highway = 'road';
-                }
-                tags.condition = 'construction';
-                break;
-        }
-
-        switch (tags.railway)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'abandoned':
-            case 'disused':
-                tags.railway = 'rail';
-                tags.condition = 'abandoned';
-                break;
-
-            case 'demolished':
-                tags.railway = 'yes';
-                tags.condition = 'dismantled';
-                break;
-
-            case 'construction':
-                if (tags.construction)
-                {
-                    tags.railway = tags.construction;
-                    delete tags.construction;
-                }
-                else
-                {
-                    tags.railway = 'rail';
-                }
-                tags.condition = 'construction';
-                break;
-        }
-
-        switch (tags.building)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'abandoned':
-            case 'disused':
-                tags.building = 'yes';
-                tags.condition = 'abandoned';
-                break;
-
-            case 'destroyed':
-                tags.building = 'yes';
-                tags.condition = 'destroyed';
-                break;
-
-            case 'demolished':
-                tags.building = 'yes';
-                tags.condition = 'dismantled';
-                break;
-
-            case 'construction':
-                if (tags.construction)
-                {
-                    tags.building = tags.construction;
-                    delete tags.construction;
-                }
-                else
-                {
-                    tags.building = 'yes';
-                }
-                tags.condition = 'construction';
-                break;
-        }
-
-        switch (tags.bridge)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'abandoned':
-            case 'disused':
-                tags.bridge = 'yes';
-                tags.condition = 'abandoned';
-                break;
-
-            case 'destroyed':
-                tags.bridge = 'yes';
-                tags.condition = 'destroyed';
-                break;
-
-            case 'demolished':
-                tags.bridge = 'yes';
-                tags.condition = 'dismantled';
-                break;
-
-            case 'construction':
-                if (tags.construction)
-                {
-                    tags.bridge = tags.construction;
-                    delete tags.construction;
-                }
-                else
-                {
-                    tags.bridge = 'yes';
-                }
-                tags.condition = 'construction';
-                break;
-        }
-
-
-        if (ggdm30.preRules == undefined)
-        {
-        // See ToOsmPostProcessing for more details about rulesList
-            var rulesList = [
-            ["t.aeroway == 'navigationaid' && t.navigationaid","delete t.navigationaid"],
-            ["t.amenity == 'bus_station'","t.public_transport = 'station'; t['transport:type'] = 'bus'"],
-            // ["t.amenity == 'marketplace'","t.facility = 'yes'"],
-            ["t.barrier == 'tank_trap' && t.tank_trap == 'dragons_teeth'","t.barrier = 'dragons_teeth'; delete t.tank_trap"],
-            ["t.communication == 'line'","t['cable:type'] = 'communication'"],
-            ["t.content && !(t.product)","t.product = t.content; delete t.content"],
-            ["t.control_tower && t.man_made == 'tower'","delete t.man_made"],
-            ["t.crossing == 'tank' && t.highway == 'crossing'","delete t.highway"],
-            ["t.diplomatic && t.amenity == 'embassy'","delete t.amenity"],
-            ["t.dock && t.waterway == 'dock'","delete t.waterway"],
-            ["t.golf == 'driving_range' && t.leisure == 'golf_course'","delete t.leisure"],
-            ["t.highway == 'bus_stop'","t['transport:type'] = 'bus'"],
-            ["t.highway == 'crossing'","t['transport:type'] = 'road';a.F_CODE = 'AQ062'; delete t.highway"],
-            ["t.highway == 'give-way'","a.F_CODE = 'AQ062'"],
-            ["t.highway == 'mini_roundabout'","t.junction = 'roundabout'"],
-            ["t.highway == 'steps'","t.highway = 'footway'"],
-            ["t.highway == 'stop'","a.F_CODE = 'AQ062'"],
-            ["t.historic == 'castle' && t.building","delete t.building"],
-            ["t.historic == 'castle' && t.ruins == 'yes'","t.condition = 'destroyed'; delete t.ruins"],
-            ["t.landcover == 'snowfield' || t.landcover == 'ice-field'","a.F_CODE = 'BJ100'"],
-            ["t.landuse == 'allotments'","t.landuse = 'farmland'"],
-            ["t.landuse == 'brownfield'","t.landuse = 'built_up_area'; t.condition = 'destroyed'"],
-            ["t.landuse == 'construction'","t.landuse = 'built_up_area'; t.condition = 'construction'"],
-            ["t.landuse == 'farm'","t.landuse = 'farmland'"],
-            ["t.landuse == 'farmland' && t.crop == 'fruit_tree'","t.landuse = 'orchard'"],
-            ["t.landuse == 'farmyard'","t.facility = 'yes'; t.use = 'agriculture'; delete t.landuse"],
-            ["t.landuse == 'grass'","t.natural = 'grassland'; t['grassland:type'] = 'grassland';"],
-            ["t.landuse == 'meadow'","t.natural = 'grassland'; t['grassland:type'] = 'meadow';"],
-            ["t.landuse == 'military'","t.military = 'installation'; delete t.landuse"],
-            ["t.landuse == 'railway' && t['railway:yard'] == 'marshalling_yard'","a.F_CODE = 'AN060'"],
-            ["t.leisure == 'recreation_ground'","t.landuse = 'recreation_ground'; delete t.leisure"],
-            ["t.landuse == 'reservoir'","t.water = 'reservoir'; delete t.landuse"],
-            ["t.landuse == 'retail'","t.landuse = 'built_up_area'; t.use = 'commercial'"],
-            ["t.landuse == 'scrub'","t.natural = 'scrub'; delete t.landuse"],
-            ["t.launch_pad","delete t.launch_pad; t.aeroway='launchpad'"],
-            ["t.leisure == 'sports_centre'","t.facility = 'yes'; t.use = 'recreation'; delete t.leisure"],
-            ["t.leisure == 'stadium' && t.building","delete t.building"],
-            ["t.man_made && t.building == 'yes'","delete t.building"],
-            ["t.man_made == 'embankment'","t.embankment = 'yes'; delete t.man_made"],
-            ["t.man_made == 'launch_pad'","delete t.man_made; t.aeroway='launchpad'"],
-            ["t.median == 'yes'","t.is_divided = 'yes'"],
-            ["t.natural == 'desert' && t.surface","t.desert_surface = t.surface; delete t.surface"],
-            ["t.natural == 'sinkhole'","a.F_CODE = 'BH145'; t['water:sink:type'] = 'sinkhole'; delete t.natural"],
-            ["t.natural == 'spring' && !(t['spring:type'])","t['spring:type'] = 'spring'"],
-            ["t.natural == 'wood'","t.landuse = 'forest'; delete t.natural"],
-            ["t.power == 'pole'","t['cable:type'] = 'power'; t['tower:shape'] = 'pole'"],
-            ["t.power == 'tower'","t['cable:type'] = 'power'"],
-            ["t.power == 'line'","t['cable:type'] = 'power'; t.cable = 'yes'; delete t.power"],
-            ["t.power == 'generator'","t.use = 'power_generation'; a.F_CODE = 'AL013'"],
-            ["t.rapids == 'yes'","t.waterway = 'rapids'; delete t.rapids"],
-            ["t.railway == 'station'","t.public_transport = 'station';  t['transport:type'] = 'railway'"],
-            ["t.railway == 'level_crossing'","t['transport:type'] = 'railway';t['transport:type2'] = 'road'; a.F_CODE = 'AQ062'; delete t.railway"],
-            ["t.railway == 'crossing'","t['transport:type'] = 'railway'; a.F_CODE = 'AQ062'; delete t.railway"],
-            ["t.resource","t.raw_material = t.resource; delete t.resource"],
-            ["t.route == 'road' && !(t.highway)","t.highway = 'road'; delete t.route"],
-            // ["(t.shop || t.office) &&  !(t.building)","a.F_CODE = 'AL013'"],
-            ["t.social_facility == 'shelter'","t.social_facility = t['social_facility:for']; delete t.amenity; delete t['social_facility:for']"],
-            ["t['tower:type'] == 'minaret' && t.man_made == 'tower'","delete t.man_made"],
-            ["t.tunnel == 'building_passage'","t.tunnel = 'yes'"],
-            ["t.use == 'islamic_prayer_hall' && t.amenity == 'place_of_worship'","delete t.amenity"],
-            ["!(t.water) && t.natural == 'water'","t.water = 'lake'"],
-            ["t.wetland && t.natural == 'wetland'","delete t.natural"],
-            ["t.water == 'river'","t.waterway = 'river'"],
-            ["t.waterway == 'riverbank'","t.waterway = 'river'"]
-            ];
-
-            ggdm30.tdsPreRules = translate.buildComplexRules(rulesList);
-        }
-
-        // Apply the rulesList
-        //translate.applyComplexRules(tags,attrs,ggdm30.tdsPreRules);
-        // Pulling this out of translate
-        for (var i = 0, rLen = ggdm30.tdsPreRules.length; i < rLen; i++)
-        {
-            if (ggdm30.tdsPreRules[i][0](tags)) ggdm30.tdsPreRules[i][1](tags,attrs);
-        }
-
-        // Fix up OSM 'walls' around facilities
-        if (tags.barrier == 'wall' && geometryType == 'Area')
-        {
-            attrs.F_CODE = 'AL010'; // Facility
-            delete tags.barrier; // Take away the walls...
-        }
-
-        // going out on a limb and processing OSM specific tags:
-        // - Building == a thing,
-        // - Amenity == The area around a thing
-        // Note: amenity=place_of_worship is a special case. It _should_ have an associated building tag
-        var facilityList = {
-            'school':'850','university':'855','college':'857','hospital':'860'
-            };
-
-        if (tags.amenity in facilityList)
-        {
-            if (geometryType == 'Area')
-            {
-                attrs.F_CODE = 'AL010'; // Facility
-            }
-            else
-            {
-                // Make sure we don't turn point facilities into buildings
-                if (!(tags.facility = 'yes'))
-                {
-                    // Debug
-                    // print('Making a building: ' + tags.facility);
-                    attrs.F_CODE = 'AL013'; // Building
-                }
-            }
-
-            // If we don't have a Feature Function then assign one
-            if (!(attrs.FFN))
-            {
-                // attrs.FFN = facilityList[tags.amenity];
-                // Debug
-                // print('PreDropped: amenity = ' + tags.amenity);
-                delete tags.amenity;
-            }
-        }
-
-        // Adding a custom rule for malls to override the rules above
-        // All of the other "shops" are buildings
-        if (tags.shop == 'mall') attrs.F_CODE = 'AG030';
-
-        // Churches etc
-        if (tags.building && ! tags.amenity)
-        {
-            var how = [ 'church','chapel','cathedral','mosque','pagoda','shrine','temple',
-                        'synagogue','tabernacle','stupa']
-            if (how.indexOf(tags.building) > -1)
-            {
-                tags.amenity = 'place_of_worship';
-            }
-
-            var rc = [ 'mission','religious_community','seminary','convent','monastry',
-                       'noviciate','hermitage','retrest','marabout']
-            if (rc.indexOf(tags.building) > -1)
-            {
-                tags.use = 'religious_activities';
-            }
+          if (geometryType == 'Line')
+          {
+            attrs.F_CODE = 'BA010'; // Land/Water Boundary - Line
+            delete tags.place;
+          }
+          else
+          {
+            // NOTE: Islands can be Points or Areas
+            attrs.F_CODE = 'BA030'; // Island
+            delete tags.natural;
+          }
         }
+        break;
 
-        // Fix up water features from OSM
-        if (tags.natural == 'water' && !(tags.water))
-        {
-            if (geometryType =='Line')
-            {
-                tags.waterway = 'river';
-                attrs.F_CODE = 'BH140';
-            }
-            else
-            {
-                tags.water = 'lake';
-                attrs.F_CODE = 'BH082';
-            }
-        }
+      } // End switch
+    }
 
-        // Cutlines/Cleared Ways & Highways
-        // This might need a cleanup
-        if (tags.man_made == 'cutline' && tags.highway)
-        {
-            if (geometryType == 'Area')
-            {
-                // Keep the cutline/cleared way
-                attrs.F_CODE = 'EC040';
+    // Bridges & Roads.  If we have an area or a line everything is fine
+    // If we have a point, we need to make sure that it becomes a bridge, not a road
+    if (tags.bridge && tags.bridge !== 'no' && geometryType =='Point') attrs.F_CODE = 'AQ040';
 
-                if (tags.memo)
-                {
-                    tags.memo = tags.memo + ';highway:' + tags.highway;
-                }
-                else
-                {
-                    tags.memo = 'highway:' + tags.highway;
-                }
 
-                delete tags.highway;
-            }
-            else
-            {
-                // Drop the cutline/cleared way
-                delete tags.man_made;
-
-                if (tags.memo)
-                {
-                    tags.memo = tags.memo + ';cleared_way:yes';
-                }
-                else
-                {
-                    tags.memo = 'cleared_way:yes';
-                }
-            }
-        }
-
-        // Places, localities and regions
-        if (tags.place)
-        {
-            switch (tags.place)
-            {
-                case 'city':
-                case 'town':
-                case 'suburb':
-                case 'neighbourhood':
-                case 'quarter':
-                case 'village':
-                case 'hamlet':
-                case 'yes':  // We set this as a default when going to OSM
-                    attrs.F_CODE = 'AL020'; // Built Up Area
-                    delete tags.place;
-                    break;
-
-                case 'isolated_dwelling':
-                    attrs.F_CODE = 'AL105'; // Settlement
-                    delete tags.place;
-                    break;
-
-                case 'populated':
-                case 'state':
-                case 'county':
-                case 'region':
-                case 'locality':
-                case 'municipality':
-                case 'borough':
-                case 'unknown':
-                    attrs.F_CODE = 'ZD045'; // Annotated Location
-                    if (tags.memo)
-                    {
-                        tags.memo = tags.memo + ';annotation:' + tags.place;
-                    }
-                    else
-                    {
-                        tags.memo = 'annotation:' + tags.place;
-                    }
-                    delete tags.place;
-                    break;
-
-                case 'island':
-                case 'islet':
-                    // If we have a coastline around an Island, decide if we are going make an Island
-                    // or a Coastline
-                    if (tags.natural == 'coastline')
-                    {
-                        if (geometryType == 'Line')
-                        {
-                            attrs.F_CODE = 'BA010'; // Land/Water Boundary - Line
-                            delete tags.place;
-                        }
-                        else
-                        {
-                            // NOTE: Islands can be Points or Areas
-                            attrs.F_CODE = 'BA030'; // Island
-                            delete tags.natural;
-                        }
-                    }
-                    break;
-
-            } // End switch
-        }
+    // Railway sidetracks
+    if (tags.service == 'siding' || tags.service == 'spur' || tags.service == 'passing' || tags.service == 'crossover')
+    {
+      tags.sidetrack = 'yes';
+      if (tags.railway) delete tags.railway;
 
-        // Bridges & Roads.  If we have an area or a line everything is fine
-        // If we have a point, we need to make sure that it becomes a bridge, not a road
-        if (tags.bridge && tags.bridge !== 'no' && geometryType =='Point') attrs.F_CODE = 'AQ040';
+    }
 
+    // Movable Bridges
+    if (tags.bridge == 'movable')
+    {
+      if (! tags['bridge:movable'])
+      {
+        	tags['bridge:movable'] = 'unknown';
+      }
+      tags.bridge = 'yes';
+      attrs.F_CODE = 'AQ040';
+    }
 
-        // Railway sidetracks
-        if (tags.service == 'siding' || tags.service == 'spur' || tags.service == 'passing' || tags.service == 'crossover')
-        {
-            tags.sidetrack = 'yes';
-            if (tags.railway) delete tags.railway;
+    // Viaducts
+    if (tags.bridge == 'viaduct')
+    {
+      tags.bridge = 'yes';
+      tags.note = translate.appendValue(tags.note,'Viaduct',';');
+    }
+
+    // Fix road junctions
+    // TDS has junctions as points. If we can make the feature into a road, railway or bridge then we will
+    // If not, it should go to the o2s layer
+    if (tags.junction && geometryType !== 'Point')
+    {
+      if (tags.highway || tags.bridge || tags.railway)
+      {
+        delete tags.junction;
+      }
+    } // End AP020 not Point
+
+    // Cables
+    if (tags.man_made == 'submarine_cable')
+    {
+      delete tags.man_made;
+      tags.cable = 'yes';
+      tags.location = 'underwater';
+    }
+
+    // "service = parking_aisle" is actually the road between rows of car spaces.
+    // If this is a line: make it into a road - the default
+    // If this is an area: make it into a car park.
+    if (tags.service == 'parking_aisle' && geometryType == 'Area')
+    {
+      delete tags.highway;
+      delete tags.service;
+      attrs.F_CODE = 'AQ140'; // Vehicle lot / car park
+    }
 
-        }
 
-        // Movable Bridges
-        if (tags.bridge == 'movable')
+    // Now use the lookup table to find an FCODE. This is here to stop clashes with the
+    // standard one2one rules
+    if (!(attrs.F_CODE) && ggdm30.fcodeLookup)
+    {
+      for (var col in tags)
+      {
+        var value = tags[col];
+        if (col in ggdm30.fcodeLookup)
         {
-          if (! tags['bridge:movable'])
+          if (value in ggdm30.fcodeLookup[col])
           {
-        	tags['bridge:movable'] = 'unknown';
+            var row = ggdm30.fcodeLookup[col][value];
+            attrs.F_CODE = row[1];
+            // Debug
+            //print('FCODE: Got ' + attrs.F_CODE);
           }
-          tags.bridge = 'yes';
-          attrs.F_CODE = 'AQ040';
-        }
-
-        // Viaducts
-        if (tags.bridge == 'viaduct')
-        {
-          tags.bridge = 'yes';
-          tags.note = translate.appendValue(tags.note,'Viaduct',';');
-        }
-
-        // Fix road junctions
-        // TDS has junctions as points. If we can make the feature into a road, railway or bridge then we will
-        // If not, it should go to the o2s layer
-        if (tags.junction && geometryType !== 'Point')
-        {
-            if (tags.highway || tags.bridge || tags.railway)
-            {
-                delete tags.junction;
-            }
-        } // End AP020 not Point
-
-        // Cables
-        if (tags.man_made == 'submarine_cable')
-        {
-            delete tags.man_made;
-            tags.cable = 'yes';
-            tags.location = 'underwater';
         }
-
-        // "service = parking_aisle" is actually the road between rows of car spaces.
-        // If this is a line: make it into a road - the default
-        // If this is an area: make it into a car park.
-        if (tags.service == 'parking_aisle' && geometryType == 'Area')
+      }
+    } // End find F_CODE
+
+    // Some tags imply that they are buildings but don't actually say so
+    // Most of these are taken from raw OSM and the OSM Wiki
+    // Not sure if the list of amenities that ARE buildings is shorter than the list of ones that
+    // are not buildings
+    // Taking "place_of_worship" out of this and making it a building
+    // NOTE: fuel is it's own FCODE so it is not on the AL013 (Building) list
+    var notBuildingList = [
+      'artwork','atm','bbq','bench','bicycle_parking','bicycle_rental','biergarten','boat_sharing','car_sharing',
+      'charging_station','clock','compressed_air','dog_bin','dog_waste_bin','drinking_water','emergency_phone',
+      'ferry_terminal','fire_hydrant','fountain','game_feeding','grass_strip','grit_bin','hunting_stand','hydrant',
+      'life_ring','loading_dock','nameplate','park','parking','parking_entrance','parking_space','picnic_table',
+      'post_box','recycling','street_light','swimming_pool','taxi','trailer_park','tricycle_station','vending_machine',
+      'waste_basket','waste_disposal','water','water_point','watering_place','yes',
+      'fuel' // NOTE: Fuel goes to a different F_CODE
+    ]; // End notBuildingList
+
+    if (!(attrs.F_CODE) && !(tags.facility) && tags.amenity && !(tags.building) && (notBuildingList.indexOf(tags.amenity) == -1)) attrs.F_CODE = 'AL013';
+
+    // If we still don't have an FCODE, try looking for individual elements
+    if (!attrs.F_CODE)
+    {
+      var fcodeMap = {
+        'highway':'AP030','railway':'AN010','building':'AL013','ford':'BH070',
+        'waterway':'BH140','bridge':'AQ040','railway:in_road':'AN010',
+        'barrier':'AP040','tourism':'AL013','junction':'AP020',
+        'mine:access':'AA010','tomb':'AL036',
+        'shop':'AL015','office':'AL015'
+
+      };
+
+      for (var i in fcodeMap)
+      {
+        if (i in tags) attrs.F_CODE = fcodeMap[i];
+      }
+    }
+
+    // Sort out PYM vs ZI032_PYM vs MCC vs VCM - Ugly
+    var pymList = [ 'AL110','AL241','AQ055','AQ110','AT042'];
+
+    var vcmList = [ 'AA040','AC020','AD010','AD025','AD030','AD041','AD050','AF010',
+      'AF020','AF021','AF030','AF040','AF070','AH055','AJ050','AJ051',
+      'AJ080','AJ085','AL010','AL013','AL019','AL080','AM011','AM020',
+      'AM030','AM070','AN076','AQ040','AQ045','AQ060','AQ116','BC050',
+      'BD115','BI010','BI050','GB230' ];
+
+    if (tags.material)
+    {
+      if (pymList.indexOf(attrs.F_CODE) !== -1)
+      {
+        tags['tower:material'] = tags.material;
+        delete tags.material;
+      }
+      else if (vcmList.indexOf(attrs.F_CODE) !== -1)
+      {
+        tags['material:vertical'] = tags.material;
+        delete tags.material;
+      }
+    }
+
+    // Protected areas have two attributes that need sorting out
+    if (tags.protection_object == 'habitat' || tags.protection_object == 'breeding_ground')
+    {
+      if (tags.protect_class) delete tags.protect_class;
+    }
+
+    // Split link roads. GGDM30 & TDSv61 now have an attribute for this
+    //        if (tags.highway && (tags['highway'].indexOf('_link') !== -1))
+    //        {
+    //            tags.highway = tags['highway'].replace('_link','');
+    //            tags.interchange_ramp = 'yes';
+    //        }
+
+
+    // Sort out "construction" etc
+    // This gets ugly due to the mix of construction, highway & railway
+    //             ["t.highway == 'construction' && t.construction","t.highway = t.construction; t.condition = 'construction'; delete t.construction"],
+    //             ["t.railway == 'construction' && t.construction","t.railway = t.construction; t.condition = 'construction'; delete t.construction"],
+    //             ["t.highway == 'construction' && !(t.construction)","t.highway = 'road'; t.condition = 'construction'"],
+    //             ["t.railway == 'construction' && !(t.construction)","t.railway = 'rail'; t.condition = 'construction'"],
+    if (tags.highway == 'construction' || tags.railway == 'construction')
+    {
+      if (tags.construction)
+      {
+        tags.condition = 'construction';
+        if (tags.railway)
         {
-            delete tags.highway;
-            delete tags.service;
-            attrs.F_CODE = 'AQ140'; // Vehicle lot / car park
+          tags.railway = tags.construction;
         }
-
-
-        // Now use the lookup table to find an FCODE. This is here to stop clashes with the
-        // standard one2one rules
-        if (!(attrs.F_CODE) && ggdm30.fcodeLookup)
-        {
-            for (var col in tags)
-            {
-                var value = tags[col];
-                if (col in ggdm30.fcodeLookup)
-                {
-                    if (value in ggdm30.fcodeLookup[col])
-                    {
-                        var row = ggdm30.fcodeLookup[col][value];
-                        attrs.F_CODE = row[1];
-                        // Debug
-                        //print('FCODE: Got ' + attrs.F_CODE);
-                    }
-                }
-            }
-        } // End find F_CODE
-
-        // Some tags imply that they are buildings but don't actually say so
-        // Most of these are taken from raw OSM and the OSM Wiki
-        // Not sure if the list of amenities that ARE buildings is shorter than the list of ones that
-        // are not buildings
-        // Taking "place_of_worship" out of this and making it a building
-        // NOTE: fuel is it's own FCODE so it is not on the AL013 (Building) list
-        var notBuildingList = [
-            'artwork','atm','bbq','bench','bicycle_parking','bicycle_rental','biergarten','boat_sharing','car_sharing',
-            'charging_station','clock','compressed_air','dog_bin','dog_waste_bin','drinking_water','emergency_phone',
-            'ferry_terminal','fire_hydrant','fountain','game_feeding','grass_strip','grit_bin','hunting_stand','hydrant',
-            'life_ring','loading_dock','nameplate','park','parking','parking_entrance','parking_space','picnic_table',
-            'post_box','recycling','street_light','swimming_pool','taxi','trailer_park','tricycle_station','vending_machine',
-            'waste_basket','waste_disposal','water','water_point','watering_place','yes',
-            'fuel' // NOTE: Fuel goes to a different F_CODE
-            ]; // End notBuildingList
-
-        if (!(attrs.F_CODE) && !(tags.facility) && tags.amenity && !(tags.building) && (notBuildingList.indexOf(tags.amenity) == -1)) attrs.F_CODE = 'AL013';
-
-        // If we still don't have an FCODE, try looking for individual elements
-        if (!attrs.F_CODE)
+        else
         {
-            var fcodeMap = {
-                'highway':'AP030','railway':'AN010','building':'AL013','ford':'BH070',
-                'waterway':'BH140','bridge':'AQ040','railway:in_road':'AN010',
-                'barrier':'AP040','tourism':'AL013','junction':'AP020',
-                'mine:access':'AA010','tomb':'AL036',
-                'shop':'AL015','office':'AL015'
-
-                           };
-
-            for (var i in fcodeMap)
-            {
-                if (i in tags) attrs.F_CODE = fcodeMap[i];
-            }
+          tags.highway = tags.construction;
         }
-
-        // Sort out PYM vs ZI032_PYM vs MCC vs VCM - Ugly
-        var pymList = [ 'AL110','AL241','AQ055','AQ110','AT042'];
-
-        var vcmList = [ 'AA040','AC020','AD010','AD025','AD030','AD041','AD050','AF010',
-                        'AF020','AF021','AF030','AF040','AF070','AH055','AJ050','AJ051',
-                        'AJ080','AJ085','AL010','AL013','AL019','AL080','AM011','AM020',
-                        'AM030','AM070','AN076','AQ040','AQ045','AQ060','AQ116','BC050',
-                        'BD115','BI010','BI050','GB230' ];
-
-        if (tags.material)
+        delete tags.construction;
+      }
+      else
+      {
+        tags.condition = 'construction';
+        if (tags.railway)
         {
-            if (pymList.indexOf(attrs.F_CODE) !== -1)
-            {
-                tags['tower:material'] = tags.material;
-                delete tags.material;
-            }
-            else if (vcmList.indexOf(attrs.F_CODE) !== -1)
-            {
-                tags['material:vertical'] = tags.material;
-                delete tags.material;
-            }
+          tags.railway = 'rail';
         }
-
-       // Protected areas have two attributes that need sorting out
-       if (tags.protection_object == 'habitat' || tags.protection_object == 'breeding_ground')
-       {
-           if (tags.protect_class) delete tags.protect_class;
-       }
-
-       // Split link roads. GGDM30 & TDSv61 now have an attribute for this
-//        if (tags.highway && (tags['highway'].indexOf('_link') !== -1))
-//        {
-//            tags.highway = tags['highway'].replace('_link','');
-//            tags.interchange_ramp = 'yes';
-//        }
-
-
-       // Sort out "construction" etc
-       // This gets ugly due to the mix of construction, highway & railway
-//             ["t.highway == 'construction' && t.construction","t.highway = t.construction; t.condition = 'construction'; delete t.construction"],
-//             ["t.railway == 'construction' && t.construction","t.railway = t.construction; t.condition = 'construction'; delete t.construction"],
-//             ["t.highway == 'construction' && !(t.construction)","t.highway = 'road'; t.condition = 'construction'"],
-//             ["t.railway == 'construction' && !(t.construction)","t.railway = 'rail'; t.condition = 'construction'"],
-       if (tags.highway == 'construction' || tags.railway == 'construction')
-       {
-           if (tags.construction)
-           {
-                tags.condition = 'construction';
-                if (tags.railway)
-                {
-                    tags.railway = tags.construction;
-                }
-                else
-                {
-                    tags.highway = tags.construction;
-                }
-                delete tags.construction;
-           }
-           else
-           {
-                tags.condition = 'construction';
-                if (tags.railway)
-                {
-                    tags.railway = 'rail';
-                }
-                else
-                {
-                    tags.highway = 'road';
-                }
-           }
-       } // End Highway & Railway construction
-
-       // Now set the relative levels and transportation types for various features
-       if (tags.highway || tags.railway)
-       {
-           if (tags.bridge && tags.bridge !== 'no')
-           {
-               ggdm30.fixTransType(tags,geometryType);
-               tags.location = 'surface';
-               tags.layer = '1';
-               tags.on_bridge = 'yes';
-           }
-
-           if (tags.tunnel && tags.tunnel !== 'no')
-           {
-               ggdm30.fixTransType(tags,geometryType);
-               // tags.layer = '-1';
-               tags.in_tunnel = 'yes';
-           }
-
-           if (tags.embankment && tags.embankment !== 'no')
-           {
-               ggdm30.fixTransType(tags,geometryType);
-               tags.layer = '1';
-           }
-
-           if (tags.cutting && tags.cutting !== 'no')
-           {
-               ggdm30.fixTransType(tags,geometryType);
-               tags.layer = '-1';
-           }
-
-           if (tags.ford && tags.ford !== 'no')
-           {
-               ggdm30.fixTransType(tags,geometryType);
-               tags.location = 'on_waterbody_bottom';
-           }
-
-       } // End if highway || railway
-
-       // Debug
-       // for (var i in tags) print('End PreProc Tags: ' + i + ': :' + tags[i] + ':');
-
-       // Tag changed
-        if (tags.vertical_obstruction_identifier)
+        else
         {
-            tags['aeroway:obstruction'] = tags.vertical_obstruction_identifier;
-            delete tags.vertical_obstruction_identifier;
+          tags.highway = 'road';
         }
+      }
+    } // End Highway & Railway construction
 
-        // Railway loading things
-        if (tags.railway == 'loading')
-        {
-            if (tags.facility == 'gantry_crane')
-            {
-                delete tags.railway;
-                delete tags.facility;
-                attrs.F_CODE = 'AF040'; // Crane
-                tags['crane:type'] = 'bridge';
-            }
-
-            if (tags.facility == 'container_terminal')
-            {
-                delete tags.railway;
-                delete tags.facility;
-                attrs.F_CODE = 'AL010'; // Facility
-                attrs.FFN = '480'; // Transportation
-            }
-        } // End loading
+    // Now set the relative levels and transportation types for various features
+    if (tags.highway || tags.railway)
+    {
+      if (tags.bridge && tags.bridge !== 'no')
+      {
+        ggdm30.fixTransType(tags,geometryType);
+        tags.location = 'surface';
+        tags.layer = '1';
+        tags.on_bridge = 'yes';
+      }
+
+      if (tags.tunnel && tags.tunnel !== 'no')
+      {
+        ggdm30.fixTransType(tags,geometryType);
+        // tags.layer = '-1';
+        tags.in_tunnel = 'yes';
+      }
+
+      if (tags.embankment && tags.embankment !== 'no')
+      {
+        ggdm30.fixTransType(tags,geometryType);
+        tags.layer = '1';
+      }
+
+      if (tags.cutting && tags.cutting !== 'no')
+      {
+        ggdm30.fixTransType(tags,geometryType);
+        tags.layer = '-1';
+      }
+
+      if (tags.ford && tags.ford !== 'no')
+      {
+        ggdm30.fixTransType(tags,geometryType);
+        tags.location = 'on_waterbody_bottom';
+      }
+
+    } // End if highway || railway
+
+    // Debug
+    // for (var i in tags) print('End PreProc Tags: ' + i + ': :' + tags[i] + ':');
+
+    // Tag changed
+    if (tags.vertical_obstruction_identifier)
+    {
+      tags['aeroway:obstruction'] = tags.vertical_obstruction_identifier;
+      delete tags.vertical_obstruction_identifier;
+    }
 
-        switch (tags.man_made)
+    // Railway loading things
+    if (tags.railway == 'loading')
+    {
+      if (tags.facility == 'gantry_crane')
+      {
+        delete tags.railway;
+        delete tags.facility;
+        attrs.F_CODE = 'AF040'; // Crane
+        tags['crane:type'] = 'bridge';
+      }
+
+      if (tags.facility == 'container_terminal')
+      {
+        delete tags.railway;
+        delete tags.facility;
+        attrs.F_CODE = 'AL010'; // Facility
+        attrs.FFN = '480'; // Transportation
+      }
+    } // End loading
+
+    switch (tags.man_made)
+    {
+    case undefined: // Break early if no value
+      break;
+
+    case 'reservoir_covered':
+      delete tags.man_made;
+      attrs.F_CODE = 'AM070'; // Storage Tank
+      tags.product = 'water';
+      break;
+
+    case 'gasometer':
+      delete tags.man_made;
+      attrs.F_CODE = 'AM070'; // Storage Tank
+      tags.product = 'gas';
+      break;
+    }
+
+    // Stop some Religion tags from stomping on Denomination tags
+    if (tags.religion && tags.denomination)
+    {
+      if (tags.religion == 'christian' || tags.religion == 'muslim')
+      {
+        switch (tags.denomination)
+        {
+        case 'roman_catholic':
+        case 'orthodox':
+        case 'protestant':
+        case 'chaldean_catholic':
+        case 'nestorian': // Not sure about this
+        case 'shia':
+        case 'sunni':
+          delete tags.religion;
+          break;
+        } // End switch
+      }
+    } // End if religion & denomination
+
+    // Names. Sometimes we don't have a name but we do have language ones
+    if (!tags.name) translate.swapName(tags);
+
+  }, // End applyToOgrPreProcessing
+
+  // #####################################################################################################
+
+  applyToOgrPostProcessing : function (tags, attrs, geometryType, notUsedTags)
+  {
+
+    // Sort out :2, :3 attributes
+    for (var i in attrs)
+    {
+      if (i.indexOf(':') > -1)
+      {
+        attrs[i.replace(':','')] = attrs[i];
+        delete attrs[i];
+      }
+    } // End :2, :3 cleaning
+
+    // Shoreline Construction (BB081) covers a lot of features
+    if (attrs.PWC) attrs.F_CODE = 'BB081';
+
+    // Inland Water Body (BH082) also covers a lot of features
+    if (attrs.IWT && !(attrs.F_CODE)) attrs.F_CODE = 'BH082';
+
+    // The follwing bit of ugly code is to account for the specs haveing two different attributes
+    // with similar names and roughly the same attributes. Bleah!
+    // Shorter but more ugly version of a set of if..else if statements
+    if (ggdm30.rules.swapListOut[attrs.F_CODE])
+    {
+      for (var i in ggdm30.rules.swapListOut[attrs.F_CODE])
+      {
+        if (i in attrs)
         {
-            case undefined: // Break early if no value
-                break;
-
-            case 'reservoir_covered':
-                delete tags.man_made;
-                attrs.F_CODE = 'AM070'; // Storage Tank
-                tags.product = 'water';
-                break;
-
-            case 'gasometer':
-                delete tags.man_made;
-                attrs.F_CODE = 'AM070'; // Storage Tank
-                tags.product = 'gas';
-                break;
+          attrs[ggdm30.rules.swapListOut[attrs.F_CODE][i]] = attrs[i];
+          delete attrs[i];
         }
+      }
+    }
 
-        // Stop some Religion tags from stomping on Denomination tags
-        if (tags.religion && tags.denomination)
-        {
-            if (tags.religion == 'christian' || tags.religion == 'muslim')
-            {
-                switch (tags.denomination)
-                {
-                    case 'roman_catholic':
-                    case 'orthodox':
-                    case 'protestant':
-                    case 'chaldean_catholic':
-                    case 'nestorian': // Not sure about this
-                    case 'shia':
-                    case 'sunni':
-                        delete tags.religion;
-                        break;
-                } // End switch
-            }
-        } // End if religion & denomination
-
-        // Names. Sometimes we don't have a name but we do have language ones
-        if (!tags.name) translate.swapName(tags);
-
-    }, // End applyToOgrPreProcessing
-
-// #####################################################################################################
-
-    applyToOgrPostProcessing : function (tags, attrs, geometryType, notUsedTags)
+    // Sort out the UUID
+    if (attrs.UFI)
     {
+      var str = attrs['UFI'].split(';');
+      attrs.UFI = str[0].replace('{','').replace('}','');
+    }
+    else if (tags['hoot:id'])
+    {
+      attrs.UID = 'raw_id:' + tags['hoot:id'];
+    }
+    else
+    {
+      if (ggdm30.config.OgrAddUuid == 'true') attrs.UFI = createUuid().replace('{','').replace('}','');
+    }
 
-        // Sort out :2, :3 attributes
-        for (var i in attrs)
+    // Add Weather Restrictions to transportation features
+    if (['AP010','AP030','AP050'].indexOf(attrs.FCODE > -1) && !attrs.ZI016_WTC )
+    {
+      switch (tags.highway)
+      {
+      case 'motorway':
+      case 'motorway_link':
+      case 'trunk':
+      case 'trunk_link':
+      case 'primary':
+      case 'primary_link':
+      case 'secondary':
+      case 'secondary_link':
+      case 'tertiary':
+      case 'tertiary_link':
+      case 'residential':
+      case 'unclassified':
+        attrs.ZI016_WTC = '1'; // All weather
+        break;
+
+      case 'track':
+        attrs.ZI016_WTC = '4'; // Limited All-weather
+        break;
+
+      case 'path':
+        attrs.ZI016_WTC = '2'; // Fair Weather
+        break;
+      }
+
+      // Use the road surface to possibly override the classification
+      // We are assumeing that unpaved roads are Fair Weather only
+      switch (attrs.ZI016_ROC)
+      {
+      case undefined: // Break early if no value
+        break;
+
+      case '1': // Unimproved
+        attrs.ZI016_WTC = '2'; // Fair Weather
+        break;
+
+      case '2': // Stabilized Earth
+      case '4': // Gravel
+        attrs.ZI016_WTC = '4'; // Limited All-weather
+        break;
+
+      case '17': // Ice
+      case '18': // Snow
+        attrs.ZI016_WTC = '3'; // Winter Only
+        break;
+
+      case '999': // Other
+        attrs.ZI016_WTC = '-999999'; // No Information
+        break;
+
+      default:
+        attrs.ZI016_WTC = '1'; // All Weather
+      }
+    } // End add weather restrictions
+
+    // Custom Road rules
+    // - Fix the "highway=" stuff that cant be done in the one2one rules
+    if (attrs.F_CODE == 'AP030' || attrs.F_CODE == 'AQ075') // Road & Ice Road
+    {
+      // If we havent fixed up the road type/class, have a go with the
+      // highway tag
+      if (!attrs.RTY && !attrs.RIN_ROI)
+      {
+        switch (tags.highway)
         {
-            if (i.indexOf(':') > -1)
-            {
-                attrs[i.replace(':','')] = attrs[i];
-                delete attrs[i]
-            }
-        } // End :2, :3 cleaning
-
-        // Shoreline Construction (BB081) covers a lot of features
-        if (attrs.PWC) attrs.F_CODE = 'BB081';
-
-        // Inland Water Body (BH082) also covers a lot of features
-        if (attrs.IWT && !(attrs.F_CODE)) attrs.F_CODE = 'BH082';
+        case 'motorway':
+        case 'motorway_link':
+          attrs.RIN_ROI = '2'; // National Motorway
+          attrs.RTY = '1'; // Motorway
+          break;
+
+        case 'trunk':
+        case 'trunk_link':
+          attrs.RIN_ROI = '3'; // National/Primary
+          attrs.RTY = '2'; // Limited Access Motorway
+          break;
+
+        case 'primary':
+        case 'primary_link':
+          attrs.RIN_ROI = '3'; // National
+          attrs.RTY = '3'; // road: Road outside a BUA
+          break;
+
+        case 'secondary':
+        case 'secondary_link':
+          attrs.RIN_ROI = '4'; // Secondary
+          attrs.RTY = '3'; // road: Road outside a BUA
+          break;
+
+        case 'tertiary':
+        case 'tertiary_link':
+          attrs.RIN_ROI = '5'; // Local
+          attrs.RTY = '3'; // road: Road outside a BUA
+          break;
+
+        case 'residential':
+        case 'unclassified':
+        case 'pedestrian':
+        case 'service':
+          attrs.RIN_ROI = '5'; // Local
+          attrs.RTY = '4'; // street: Road inside a BUA
+          break;
+
+        case 'road':
+          attrs.RIN_ROI = '5'; // Local. Customer requested this translation value
+          attrs.RTY = '-999999'; // No Information
+        } // End tags.highway switch
+
+      } // End ROI & RIN_ROI
+
+      // Use the Width to populate the Minimum Travelled Way Width - Customer requested
+      if (attrs.WID && !(attrs.ZI016_WD1))
+      {
+        attrs.ZI016_WD1 = attrs.WID;
+        delete attrs.WID;
+      }
+
+      // Private Access roads - Customer requested
+      if (tags.access == 'private' && !(attrs.CAA))
+      {
+        attrs.CAA = '3';
+        delete notUsedTags.access;
+      }
+
+      // Fix up RLE
+      // If Vertical Relative Location != Surface && Not on a Bridge, Relative Level == NA
+      if ((attrs.LOC && attrs.LOC !== '44') && (attrs.SBB && attrs.SBB == '1000'))
+      {
+        attrs.RLE = '998';
+      }
+
+    }
+
+    // RLE vs LOC: Need to deconflict this for various features
+    // This is the list of features that can be "Above Surface". Other features use RLE (Relative Level) instead
+    if (attrs.LOC == '45' && (['AT005','AQ113','BH065','BH110'].indexOf(attrs.TRS) == -1))
+    {
+      attrs.RLE = '2'; // Raised above surface
+      attrs.LOC = '44'; // On Surface
+    }
 
-        // The follwing bit of ugly code is to account for the specs haveing two different attributes
-        // with similar names and roughly the same attributes. Bleah!
-        // Shorter but more ugly version of a set of if..else if statements
-        if (ggdm30.rules.swapListOut[attrs.F_CODE])
-        {
-            for (var i in ggdm30.rules.swapListOut[attrs.F_CODE])
-            {
-                if (i in attrs)
-                {
-                    attrs[ggdm30.rules.swapListOut[attrs.F_CODE][i]] = attrs[i];
-                    delete attrs[i]
-                }
-            }
-        }
+    // Clean up Cart Track attributes
+    if (attrs.F_CODE == 'AP010')
+    {
+      if (attrs.TRS && (['3','4','6','11','21','22','999'].indexOf(attrs.TRS) == -1))
+      {
+        var othVal = '(TRS:' + attrs.TRS + ')';
+        attrs.OTH = translate.appendValue(attrs.OTH,othVal,' ');
+        attrs.TRS = '999';
+
+      }
+    }
+
+    // Fix HGT and LMC to keep GAIT happy
+    // If things have a height greater than 46m, tags them as being a "Navigation Landmark"
+    if (attrs.HGT > 46 && !(attrs.LMC)) attrs.LMC = '1001';
+
+    // The ZI001_SDV (source date time) field can only be 20 characters long. When we conflate features,
+    // we concatenate the tag values for this field
+    // We are getting guidance from the customer on what value they would like in this field:
+    // * The earliest date/time,
+    // * The first on in the list
+    // * etc
+    //
+    // Until we get an answer, we are going to take the first value in the list
+    if (attrs.ZI001_SDV)
+    {
+      attrs.ZI001_SDV = translate.chopDateTime(attrs.ZI001_SDV);
+    }
 
-        // Sort out the UUID
-        if (attrs.UFI)
-        {
-            var str = attrs['UFI'].split(';');
-            attrs.UFI = str[0].replace('{','').replace('}','');
-        }
-        else if (tags['hoot:id'])
+    // Fix the ZI020_GE4X Values
+    // NOTE: This is the opposite to what is done in the toOSM post processing
+    var ge4attr = ['ZI020_GE4','ZI020_GE42','ZI020_GE43','ZI020_GE44','ZI020_GE4A','ZI020_GE4B' ];
+    for (var i=0, iLen=ge4attr.length; i < iLen; i++)
+    {
+      if (attrs[ge4attr[i]])
+      {
+        if (ggdm30.ge4Lookup[attrs[ge4attr[i]]])
         {
-            attrs.UID = 'raw_id:' + tags['hoot:id'];
+          attrs[ge4attr[i]] = ggdm30.ge4Lookup[attrs[ge4attr[i]]];
         }
         else
         {
-            if (ggdm30.config.OgrAddUuid == 'true') attrs.UFI = createUuid().replace('{','').replace('}','');
-        }
-
-        // Add Weather Restrictions to transportation features
-        if (['AP010','AP030','AP050'].indexOf(attrs.FCODE > -1) && !attrs.ZI016_WTC )
-        {
-            switch (tags.highway)
-            {
-                case 'motorway':
-                case 'motorway_link':
-                case 'trunk':
-                case 'trunk_link':
-                case 'primary':
-                case 'primary_link':
-                case 'secondary':
-                case 'secondary_link':
-                case 'tertiary':
-                case 'tertiary_link':
-                case 'residential':
-                case 'unclassified':
-                    attrs.ZI016_WTC = '1'; // All weather
-                    break;
-
-                case 'track':
-                    attrs.ZI016_WTC = '4'; // Limited All-weather
-                    break;
-
-                case 'path':
-                    attrs.ZI016_WTC = '2'; // Fair Weather
-                    break;
-            }
-
-            // Use the road surface to possibly override the classification
-            // We are assumeing that unpaved roads are Fair Weather only
-            switch (attrs.ZI016_ROC)
-            {
-                case undefined: // Break early if no value
-                    break;
-
-                case '1': // Unimproved
-                    attrs.ZI016_WTC = '2'; // Fair Weather
-                    break;
-
-                case '2': // Stabilized Earth
-                case '4': // Gravel
-                    attrs.ZI016_WTC = '4'; // Limited All-weather
-                    break;
-
-                case '17': // Ice
-                case '18': // Snow
-                    attrs.ZI016_WTC = '3'; // Winter Only
-                    break;
-
-                case '999': // Other
-                    attrs.ZI016_WTC = '-999999'; // No Information
-                    break;
-
-                default:
-                    attrs.ZI016_WTC = '1' // All Weather
-            }
-        } // End add weather restrictions
-
-        // Custom Road rules
-        // - Fix the "highway=" stuff that cant be done in the one2one rules
-        if (attrs.F_CODE == 'AP030' || attrs.F_CODE == 'AQ075') // Road & Ice Road
-        {
-            // If we havent fixed up the road type/class, have a go with the
-            // highway tag
-            if (!attrs.RTY && !attrs.RIN_ROI)
-            {
-                switch (tags.highway)
-                {
-                    case 'motorway':
-                    case 'motorway_link':
-                        attrs.RIN_ROI = '2'; // National Motorway
-                        attrs.RTY = '1'; // Motorway
-                        break;
-
-                    case 'trunk':
-                    case 'trunk_link':
-                        attrs.RIN_ROI = '3'; // National/Primary
-                        attrs.RTY = '2'; // Limited Access Motorway
-                        break;
-
-                    case 'primary':
-                    case 'primary_link':
-                        attrs.RIN_ROI = '3'; // National
-                        attrs.RTY = '3'; // road: Road outside a BUA
-                        break;
-
-                    case 'secondary':
-                    case 'secondary_link':
-                        attrs.RIN_ROI = '4'; // Secondary
-                        attrs.RTY = '3'; // road: Road outside a BUA
-                        break;
-
-                    case 'tertiary':
-                    case 'tertiary_link':
-                        attrs.RIN_ROI = '5'; // Local
-                        attrs.RTY = '3'; // road: Road outside a BUA
-                        break;
-
-                    case 'residential':
-                    case 'unclassified':
-                    case 'pedestrian':
-                    case 'service':
-                        attrs.RIN_ROI = '5'; // Local
-                        attrs.RTY = '4'; // street: Road inside a BUA
-                        break;
-
-                    case 'road':
-                        attrs.RIN_ROI = '5'; // Local. Customer requested this translation value
-                        attrs.RTY = '-999999'; // No Information
-                } // End tags.highway switch
-
-            } // End ROI & RIN_ROI
-
-            // Use the Width to populate the Minimum Travelled Way Width - Customer requested
-            if (attrs.WID && !(attrs.ZI016_WD1))
-            {
-                attrs.ZI016_WD1 = attrs.WID;
-                delete attrs.WID;
-            }
-
-            // Private Access roads - Customer requested
-            if (tags.access == 'private' && !(attrs.CAA))
-            {
-                attrs.CAA = '3';
-                delete notUsedTags.access;
-            }
-
-            // Fix up RLE
-            // If Vertical Relative Location != Surface && Not on a Bridge, Relative Level == NA
-            if ((attrs.LOC && attrs.LOC !== '44') && (attrs.SBB && attrs.SBB == '1000'))
-            {
-                attrs.RLE = '998';
-            }
-
-        }
-
-        // RLE vs LOC: Need to deconflict this for various features
-        // This is the list of features that can be "Above Surface". Other features use RLE (Relative Level) instead
-        if (attrs.LOC == '45' && (['AT005','AQ113','BH065','BH110'].indexOf(attrs.TRS) == -1))
-        {
-            attrs.RLE = '2'; // Raised above surface
-            attrs.LOC = '44'; // On Surface
+          hoot.logWarn('Dropping invalid ' + ge4attr[i] + ' value: ' + attrs[ge4attr[i]]);
+          delete attrs[ge4attr[i]];
         }
+      }
+    } // End for GE4 loop
 
-        // Clean up Cart Track attributes
-        if (attrs.F_CODE == 'AP010')
-        {
-            if (attrs.TRS && (['3','4','6','11','21','22','999'].indexOf(attrs.TRS) == -1))
-            {
-                var othVal = '(TRS:' + attrs.TRS + ')';
-                attrs.OTH = translate.appendValue(attrs.OTH,othVal,' ');
-                attrs.TRS = '999';
-
-            }
-        }
-
-        // Fix HGT and LMC to keep GAIT happy
-        // If things have a height greater than 46m, tags them as being a "Navigation Landmark"
-        if (attrs.HGT > 46 && !(attrs.LMC)) attrs.LMC = '1001';
-
-        // The ZI001_SDV (source date time) field can only be 20 characters long. When we conflate features,
-        // we concatenate the tag values for this field
-        // We are getting guidance from the customer on what value they would like in this field:
-        // * The earliest date/time,
-        // * The first on in the list
-        // * etc
-        //
-        // Until we get an answer, we are going to take the first value in the list
-        if (attrs.ZI001_SDV)
-        {
-            attrs.ZI001_SDV = translate.chopDateTime(attrs.ZI001_SDV);
-        }
-
-        // Fix the ZI020_GE4X Values
-        // NOTE: This is the opposite to what is done in the toOSM post processing
-        var ge4attr = ['ZI020_GE4','ZI020_GE42','ZI020_GE43','ZI020_GE44','ZI020_GE4A','ZI020_GE4B' ]
-        for (var i=0, iLen=ge4attr.length; i < iLen; i++)
-        {
-            if (attrs[ge4attr[i]])
-            {
-                if (ggdm30.ge4Lookup[attrs[ge4attr[i]]])
-                {
-                    attrs[ge4attr[i]] = ggdm30.ge4Lookup[attrs[ge4attr[i]]];
-                }
-                else
-                {
-                    hoot.logWarn('Dropping invalid ' + ge4attr[i] + ' value: ' + attrs[ge4attr[i]]);
-                    delete attrs[ge4attr[i]];
-                }
-            }
-        } // End for GE4 loop
-
-        //Map alternate source date tags to ZI001_SDV in order of precedence
-        //default is 'source:datetime'
-        if (! attrs.ZI001_SDV)
-            attrs.ZI001_SDV = tags['source:imagery:datetime']
+    //Map alternate source date tags to ZI001_SDV in order of precedence
+    //default is 'source:datetime'
+    if (! attrs.ZI001_SDV)
+      attrs.ZI001_SDV = tags['source:imagery:datetime']
                 || tags['source:date']
                 || tags['source:geometry:date']
                 || '';
 
-        //Map alternate source tags to ZI001_SDP in order of precedence
-        //default is 'source'
-        if (! attrs.ZI001_SDP)
-            attrs.ZI001_SDP = tags['source:imagery']
+    //Map alternate source tags to ZI001_SDP in order of precedence
+    //default is 'source'
+    if (! attrs.ZI001_SDP)
+      attrs.ZI001_SDP = tags['source:imagery']
                 || tags['source:description']
                 || '';
 
-        // Wetlands
-        // Normally, these go to Marsh
-        switch(tags.wetland)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'mangrove':
-                attrs.F_CODE = 'ED020'; // Swamp
-                attrs.VSP = '19'; // Mangrove
-                break;
-        } // End Wetlands
-    }, // End applyToOgrPostProcessing
-
-// #####################################################################################################
-
-    // ##### End of the xxToOgrxx Block #####
-
-    // toOsm - Translate Attrs to Tags
-    // This is the main routine to convert _TO_ OSM
-    toOsm : function(attrs, layerName, geometryType)
+    // Wetlands
+    // Normally, these go to Marsh
+    switch(tags.wetland)
     {
-        tags = {};  // The final output Tag list
+    case undefined: // Break early if no value
+      break;
 
-        // Setup config variables. We could do this in initialize() but some things don't call it :-(
-        // Doing this so we don't have to keep calling into Hoot core
-        if (ggdm30.config == undefined)
-        {
-            ggdm30.config = {};
-            ggdm30.config.OgrDebugAddfcode = config.getOgrDebugAddfcode();
-            ggdm30.config.OgrDebugDumptags = config.getOgrDebugDumptags();
-            ggdm30.config.OgrAddUuid = config.getOgrAddUuid();
-
-            // Get any changes to OSM tags
-            // NOTE: the rest of the config variables will change to this style of assignment soon
-            ggdm30.toChange = hoot.Settings.get("schema.translation.override");
-        }
+    case 'mangrove':
+      attrs.F_CODE = 'ED020'; // Swamp
+      attrs.VSP = '19'; // Mangrove
+      break;
+    } // End Wetlands
+  }, // End applyToOgrPostProcessing
 
-        // Debug:
-        if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(attrs,layerName,geometryType,'','In attrs: ');
+  // #####################################################################################################
 
-        // See if we have an o2s_X layer and try to unpack it
-        if (layerName.indexOf('o2s_') > -1)
-        {
-            tags = translate.parseO2S(attrs);
+  // ##### End of the xxToOgrxx Block #####
 
-            // Add some metadata
-            if (! tags.uuid)
-            {
-                if (ggdm30.config.OgrAddUuid == 'true') tags.uuid = createUuid();
-            }
+  // toOsm - Translate Attrs to Tags
+  // This is the main routine to convert _TO_ OSM
+  toOsm : function(attrs, layerName, geometryType)
+  {
+    tags = {};  // The final output Tag list
 
-            if (! tags.source) tags.source = 'ggdmv30:' + layerName.toLowerCase();
-
-            // Debug:
-            if (ggdm30.config.OgrDebugDumptags == 'true')
-            {
-                translate.debugOutput(tags,layerName,geometryType,'','Out tags: ');
-                print('');
-            }
+    // Setup config variables. We could do this in initialize() but some things don't call it :-(
+    // Doing this so we don't have to keep calling into Hoot core
+    if (ggdm30.config == undefined)
+    {
+      ggdm30.config = {};
+      ggdm30.config.OgrDebugAddfcode = config.getOgrDebugAddfcode();
+      ggdm30.config.OgrDebugDumptags = config.getOgrDebugDumptags();
+      ggdm30.config.OgrAddUuid = config.getOgrAddUuid();
 
-            return tags;
-        } // End layername = o2s_X
+      // Get any changes to OSM tags
+      // NOTE: the rest of the config variables will change to this style of assignment soon
+      ggdm30.toChange = hoot.Settings.get('schema.translation.override');
+    }
 
-        // Set up the fcode translation rules. We need this due to clashes between the one2one and
-        // the fcode one2one rules
-        if (ggdm30.fcodeLookup == undefined)
-        {
-            // Add the FCODE rules for Import
-            fcodeCommon.one2one.push.apply(fcodeCommon.one2one,ggdm30.rules.fcodeOne2oneIn);
+    // Debug:
+    if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(attrs,layerName,geometryType,'','In attrs: ');
 
-            ggdm30.fcodeLookup = translate.createLookup(fcodeCommon.one2one);
-            // Debug
-//             print('Start Dump FCODE:');
-//             translate.dumpOne2OneLookup(ggdm30.fcodeLookup);
-//             print('End Dump FCODE:');
-        }
+    // See if we have an o2s_X layer and try to unpack it
+    if (layerName.indexOf('o2s_') > -1)
+    {
+      tags = translate.parseO2S(attrs);
 
-        if (ggdm30.lookup == undefined)
-        {
-            // Setup lookup tables to make translation easier. I'm assumeing that since this is not set, the
-            // other tables are not set either
+      // Add some metadata
+      if (! tags.uuid)
+      {
+        if (ggdm30.config.OgrAddUuid == 'true') tags.uuid = createUuid();
+      }
 
-            // Support Import Only attributes
-            ggdm30.rules.one2one.push.apply(ggdm30.rules.one2one,ggdm30.rules.one2oneIn);
+      if (! tags.source) tags.source = 'ggdmv30:' + layerName.toLowerCase();
 
-            ggdm30.lookup = translate.createLookup(ggdm30.rules.one2one);
-        }
+      // Debug:
+      if (ggdm30.config.OgrDebugDumptags == 'true')
+      {
+        translate.debugOutput(tags,layerName,geometryType,'','Out tags: ');
+        print('');
+      }
 
-        // pre processing
-        ggdm30.applyToOsmPreProcessing(attrs, layerName, geometryType);
+      return tags;
+    } // End layername = o2s_X
 
-        // Use the FCODE to add some tags
-        if (attrs.F_CODE)
-        {
-            var ftag = ggdm30.fcodeLookup['F_CODE'][attrs.F_CODE];
-            if (ftag)
-            {
-                tags[ftag[0]] = ftag[1];
-                // Debug: Dump out the tags from the FCODE
-                // print('FCODE: ' + attrs.F_CODE + ' tag=' + ftag[0] + '  value=' + ftag[1]);
-            }
-            else
-            {
-                hoot.logTrace('Translation for F_CODE ' + attrs.F_CODE + ' not found');
-            }
-        }
+    // Set up the fcode translation rules. We need this due to clashes between the one2one and
+    // the fcode one2one rules
+    if (ggdm30.fcodeLookup == undefined)
+    {
+      // Add the FCODE rules for Import
+      fcodeCommon.one2one.push.apply(fcodeCommon.one2one,ggdm30.rules.fcodeOne2oneIn);
 
-        // Make a copy of the input attributes so we can remove them as they get translated. Looking at what
-        // isn't used in the translation - this should end up empty
-        // not in v8 yet: // var tTags = Object.assign({},tags);
-        var notUsedAttrs = (JSON.parse(JSON.stringify(attrs)));
-        delete notUsedAttrs.F_CODE; // Not needed since we have it in attrs
+      ggdm30.fcodeLookup = translate.createLookup(fcodeCommon.one2one);
+      // Debug
+      //             print('Start Dump FCODE:');
+      //             translate.dumpOne2OneLookup(ggdm30.fcodeLookup);
+      //             print('End Dump FCODE:');
+    }
 
-        // apply the simple number and text biased rules
-        // NOTE: We are not using the intList paramater for applySimpleNumBiased when going to OSM
-        translate.applySimpleNumBiased(notUsedAttrs, tags, ggdm30.rules.numBiased,'forward',[]);
-        translate.applySimpleTxtBiased(notUsedAttrs, tags, ggdm30.rules.txtBiased,'forward');
+    if (ggdm30.lookup == undefined)
+    {
+      // Setup lookup tables to make translation easier. I'm assumeing that since this is not set, the
+      // other tables are not set either
 
-        // one 2 one
-        //translate.applyOne2One(notUsedAttrs, tags, ggdm30.lookup, {'k':'v'});
-        translate.applyOne2OneQuiet(notUsedAttrs, tags, ggdm30.lookup);
+      // Support Import Only attributes
+      ggdm30.rules.one2one.push.apply(ggdm30.rules.one2one,ggdm30.rules.one2oneIn);
 
-        // Translate the XXX2, XXX3 etc attributes
-        translate.fix23Attr(notUsedAttrs, tags, ggdm30.lookup);
+      ggdm30.lookup = translate.createLookup(ggdm30.rules.one2one);
+    }
 
-        // Crack open the OTH field and populate the appropriate attributes
-        if (attrs.OTH) translate.processOTH(attrs, tags, ggdm30.lookup);
+    // pre processing
+    ggdm30.applyToOsmPreProcessing(attrs, layerName, geometryType);
 
-        // post processing
-        ggdm30.applyToOsmPostProcessing(attrs, tags, layerName, geometryType);
+    // Use the FCODE to add some tags
+    if (attrs.F_CODE)
+    {
+      var ftag = ggdm30.fcodeLookup['F_CODE'][attrs.F_CODE];
+      if (ftag)
+      {
+        tags[ftag[0]] = ftag[1];
+        // Debug: Dump out the tags from the FCODE
+        // print('FCODE: ' + attrs.F_CODE + ' tag=' + ftag[0] + '  value=' + ftag[1]);
+      }
+      else
+      {
+        hoot.logTrace('Translation for F_CODE ' + attrs.F_CODE + ' not found');
+      }
+    }
+
+    // Make a copy of the input attributes so we can remove them as they get translated. Looking at what
+    // isn't used in the translation - this should end up empty
+    // not in v8 yet: // var tTags = Object.assign({},tags);
+    var notUsedAttrs = (JSON.parse(JSON.stringify(attrs)));
+    delete notUsedAttrs.F_CODE; // Not needed since we have it in attrs
+
+    // apply the simple number and text biased rules
+    // NOTE: We are not using the intList paramater for applySimpleNumBiased when going to OSM
+    translate.numToOSM(notUsedAttrs, tags, ggdm30.rules.numBiased,);
+    translate.txtToOSM(notUsedAttrs, tags, ggdm30.rules.txtBiased);
+
+    // one 2 one
+    //translate.applyOne2One(notUsedAttrs, tags, ggdm30.lookup, {'k':'v'});
+    translate.applyOne2OneQuiet(notUsedAttrs, tags, ggdm30.lookup,{'k':'v'});
+
+    // Translate the XXX2, XXX3 etc attributes
+    translate.fix23Attr(notUsedAttrs, tags, ggdm30.lookup);
+
+    // Crack open the OTH field and populate the appropriate attributes
+    if (attrs.OTH) translate.processOTH(attrs, tags, ggdm30.lookup);
+
+    // post processing
+    ggdm30.applyToOsmPostProcessing(attrs, tags, layerName, geometryType);
+
+    // Debug: Add the FCODE to the tags
+    if (ggdm30.config.OgrDebugAddfcode == 'true') tags['raw:debugFcode'] = attrs.F_CODE;
+
+    // Override tag values if appropriate
+    translate.overrideValues(tags,ggdm30.toChange);
+
+    // Debug:
+    if (ggdm30.config.OgrDebugDumptags == 'true')
+    {
+      translate.debugOutput(notUsedAttrs,layerName,geometryType,'','Not used: ');
+      translate.debugOutput(tags,layerName,geometryType,'','Out tags: ');
+      print('');
+    }
+
+    return tags;
+  }, // End of toOsm
+
+
+  // This gets called by translateToOGR and is where the main work gets done
+  // We get Tags and return Attrs and a tableName
+  // This is the main routine to convert _TO_ GGDM
+  toOgr : function(tags, elementType, geometryType)
+  {
+    var tableName = ''; // The final table name
+    var returnData = []; // The array of features to return
+    var transMap = {}; // A map of translated attributes
+    attrs = {}; // The output attributes
+    attrs.F_CODE = ''; // Initial setup
+
+    // Setup config variables. We could do this in initialize() but some things don't call it :-(
+    // Doing this so we don't have to keep calling into Hoot core
+    if (ggdm30.config == undefined)
+    {
+      ggdm30.config = {};
+      ggdm30.config.OgrDebugDumptags = config.getOgrDebugDumptags();
+      ggdm30.config.OgrEsriFcsubtype = config.getOgrEsriFcsubtype();
+      ggdm30.config.OgrNoteExtra = config.getOgrNoteExtra();
+      ggdm30.config.OgrSplitO2s = config.getOgrSplitO2s();
+      ggdm30.config.OgrThematicStructure = config.getOgrThematicStructure();
+      ggdm30.config.OgrThrowError = config.getOgrThrowError();
+      ggdm30.config.OgrAddUuid = config.getOgrAddUuid();
+
+      // Get any changes to OSM tags
+      // NOTE: the rest of the config variables will change to this style of assignment soon
+      ggdm30.toChange = hoot.Settings.get('schema.translation.override');
+    }
+
+    // Check if we have a schema. This is a quick way to workout if various lookup tables have been built
+    if (ggdm30.rawSchema == undefined)
+    {
+      var tmp_schema = ggdm30.getDbSchema();
+    }
 
-        // Debug: Add the FCODE to the tags
-        if (ggdm30.config.OgrDebugAddfcode == 'true') tags['raw:debugFcode'] = attrs.F_CODE;
+    // The Nuke Option: If we have a relation, drop the feature and carry on
+    if (tags['building:part']) return null;
 
-        // Override tag values if appropriate
-        translate.overrideValues(tags,ggdm30.toChange);
+    // The Nuke Option: "Collections" are groups of different geometry types: Point, Area and Line
+    // There is no way we can translate these to a single GGDM30 feature
+    if (geometryType == 'Collection') return null;
 
-        // Debug:
-        if (ggdm30.config.OgrDebugDumptags == 'true')
-        {
-            translate.debugOutput(notUsedAttrs,layerName,geometryType,'','Not used: ');
-            translate.debugOutput(tags,layerName,geometryType,'','Out tags: ');
-            print('');
-        }
+    // Start processing here
+    // Debug:
+    if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(tags,'',geometryType,elementType,'In tags: ');
 
-        return tags;
-    }, // End of toOsm
+    // Flip the ge4List table so we can use it for export
+    if (ggdm30.ge4Lookup == undefined)
+    {
+      ggdm30.ge4Lookup = {};
+      for (var i in ggdm30.rules.ge4List)
+      {
+        ggdm30.ge4Lookup[ggdm30.rules.ge4List[i]] = i;
+      }
+    }
+
+    // Set up the fcode translation rules. We need this due to clashes between the one2one and
+    // the fcode one2one rules
+    if (ggdm30.fcodeLookup == undefined)
+    {
+      // Add the FCODE rules for Export
+      fcodeCommon.one2one.push.apply(fcodeCommon.one2one,ggdm30.rules.fcodeOne2oneOut);
 
+      ggdm30.fcodeLookup = translate.createBackwardsLookup(fcodeCommon.one2one);
+      // Debug
+      // translate.dumpOne2OneLookup(ggdm30.fcodeLookup);
+    }
 
-    // This gets called by translateToOGR and is where the main work gets done
-    // We get Tags and return Attrs and a tableName
-    // This is the main routine to convert _TO_ GGDM
-    toOgr : function(tags, elementType, geometryType)
+    if (ggdm30.lookup == undefined)
     {
-        var tableName = ''; // The final table name
-        var returnData = []; // The array of features to return
-        attrs = {}; // The output attributes
-        attrs.F_CODE = ''; // Initial setup
-
-        // Setup config variables. We could do this in initialize() but some things don't call it :-(
-        // Doing this so we don't have to keep calling into Hoot core
-        if (ggdm30.config == undefined)
-        {
-            ggdm30.config = {};
-            ggdm30.config.OgrDebugDumptags = config.getOgrDebugDumptags();
-            ggdm30.config.OgrEsriFcsubtype = config.getOgrEsriFcsubtype();
-            ggdm30.config.OgrNoteExtra = config.getOgrNoteExtra();
-            ggdm30.config.OgrSplitO2s = config.getOgrSplitO2s();
-            ggdm30.config.OgrThematicStructure = config.getOgrThematicStructure();
-            ggdm30.config.OgrThrowError = config.getOgrThrowError();
-            ggdm30.config.OgrAddUuid = config.getOgrAddUuid();
-
-            // Get any changes to OSM tags
-            // NOTE: the rest of the config variables will change to this style of assignment soon
-            ggdm30.toChange = hoot.Settings.get("schema.translation.override");
-        }
+      // Add "other" rules to the one2one
+      ggdm30.rules.one2one.push.apply(ggdm30.rules.one2one,ggdm30.rules.one2oneOut);
+
+      ggdm30.lookup = translate.createBackwardsLookup(ggdm30.rules.one2one);
+      // Debug
+      //translate.dumpOne2OneLookup(ggdm30.lookup);
+
+      // Make the fuzzy lookup table
+      ggdm30.fuzzy = schemaTools.generateToOgrTable(ggdm30.rules.fuzzyTable);
+
+      // Debug
+      //             for (var k1 in ggdm30.fuzzy)
+      //             {
+      //                 for (var v1 in ggdm30.fuzzy[k1])
+      //                 {
+      //                     print(JSON.stringify([k1, v1, ggdm30.fuzzy[k1][v1][0], ggdm30.fuzzy[k1][v1][1], ggdm30.fuzzy[k1][v1][2]]));
+      //                 }
+      //             }
+    } // End ggdm30.lookup Undefined
+
+    // Override values if appropriate
+    translate.overrideValues(tags,ggdm30.toChange);
+
+    // Pre Processing
+    ggdm30.applyToOgrPreProcessing(tags, attrs, geometryType);
+
+    // Make a copy of the input tags so we can remove them as they get translated. What is left is
+    // the not used tags
+    // not in v8 yet: // var tTags = Object.assign({},tags);
+    var notUsedTags = (JSON.parse(JSON.stringify(tags)));
+
+    if (notUsedTags.hoot) delete notUsedTags.hoot; // Added by the UI
+    // Debug info. We use this in postprocessing via "tags"
+    if (notUsedTags['hoot:id']) delete notUsedTags['hoot:id'];
+
+    // Apply the simple number and text biased rules
+    // NOTE: These are BACKWARD, not forward!
+    // NOTE: These delete tags as they are used
+    translate.numToOgr(attrs, notUsedTags, ggdm30.rules.numBiased,ggdm30.rules.intList,transMap);
+    translate.txtToOgr(attrs, notUsedTags, ggdm30.rules.txtBiased,transMap);
+
+    // Apply the fuzzy rules
+    // NOTE: This deletes tags as they are used
+    translate.applyOne2OneQuiet(notUsedTags, attrs, ggdm30.fuzzy,{'k':'v'});
+
+    // Translate the XXX:2, XXX2, XXX:3 etc attributes
+    // Note: This deletes tags as they are used
+    translate.fix23Tags(notUsedTags, attrs, ggdm30.lookup);
+
+    // one 2 one: we call the version that knows about the OTH field
+    // NOTE: This deletes tags as they are used
+    translate.applyTdsOne2One(notUsedTags, attrs, ggdm30.lookup, ggdm30.fcodeLookup,transMap);
+
+    // Post Processing
+    // We send the original list of tags and the list of tags we haven't used yet
+    // ggdm30.applyToOgrPostProcessing(tags, attrs, geometryType);
+    ggdm30.applyToOgrPostProcessing(tags, attrs, geometryType, notUsedTags);
+
+    // Debug
+    if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(notUsedTags,'',geometryType,elementType,'Not used: ');
+
+    // If we have unused tags, add them to the memo field
+    if (Object.keys(notUsedTags).length > 0 && ggdm30.config.OgrNoteExtra == 'attribute')
+    {
+      var tStr = '<OSM>' + JSON.stringify(notUsedTags) + '</OSM>';
+      attrs.ZI006_MEM = translate.appendValue(attrs.ZI006_MEM,tStr,';');
+    }
 
-        // Check if we have a schema. This is a quick way to workout if various lookup tables have been built
-        if (ggdm30.rawSchema == undefined)
-        {
-            var tmp_schema = ggdm30.getDbSchema();
-        }
+    // Now check for invalid feature geometry
+    // E.g. If the spec says a runway is a polygon and we have a line, throw error and
+    // push the feature to o2s layer
+    var gFcode = geometryType.toString().charAt(0) + attrs.F_CODE;
 
-        // The Nuke Option: If we have a relation, drop the feature and carry on
-        if (tags['building:part']) return null;
+    if (ggdmAttrLookup[gFcode])
+    {
+      // Check if we need to make more features
+      // NOTE: This returns structure we are going to send back to Hoot:  {attrs: attrs, tableName: 'Name'}
+      returnData = ggdm30.manyFeatures(geometryType,tags,attrs,transMap);
 
-        // The Nuke Option: "Collections" are groups of different geometry types: Point, Area and Line
-        // There is no way we can translate these to a single GGDM30 feature
-        if (geometryType == 'Collection') return null;
+      // Debug: Add the first feature
+      //returnData.push({attrs: attrs, tableName: ''});
 
-        // Start processing here
-        // Debug:
-        if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(tags,'',geometryType,elementType,'In tags: ');
+      // Now go through the features and clean them up
+      var gType = geometryType.toString().charAt(0);
+      for (var i = 0, fLen = returnData.length; i < fLen; i++)
+      {
+        var gFcode = gType + returnData[i]['attrs']['F_CODE'];
 
-        // Flip the ge4List table so we can use it for export
-        if (ggdm30.ge4Lookup == undefined)
+        if (ggdmAttrLookup[gFcode.toUpperCase()])
         {
-            ggdm30.ge4Lookup = {};
-            for (var i in ggdm30.rules.ge4List)
-            {
-                ggdm30.ge4Lookup[ggdm30.rules.ge4List[i]] = i;
-            }
-        }
+          // Validate attrs: remove all that are not supposed to be part of a feature
+          ggdm30.validateAttrs(geometryType,returnData[i]['attrs'],notUsedTags,transMap);
 
-        // Set up the fcode translation rules. We need this due to clashes between the one2one and
-        // the fcode one2one rules
-        if (ggdm30.fcodeLookup == undefined)
-        {
-            // Add the FCODE rules for Export
-            fcodeCommon.one2one.push.apply(fcodeCommon.one2one,ggdm30.rules.fcodeOne2oneOut);
+          // Now set the FCSubtype
+          // NOTE: If we export to shapefile, GAIT _will_ complain about this
+          if (ggdm30.config.OgrEsriFcsubtype == 'true')
+          {
+            returnData[i]['attrs']['FCSUBTYPE'] = ggdm30.rules.subtypeList[returnData[i]['attrs']['F_CODE']];
+          }
 
-            ggdm30.fcodeLookup = translate.createBackwardsLookup(fcodeCommon.one2one);
-            // Debug
-            // translate.dumpOne2OneLookup(ggdm30.fcodeLookup);
+          // If we are using the Thematic structure, fill the rest of the unused attrs in the schema
+          if (ggdm30.config.OgrThematicStructure == 'true')
+          {
+            returnData[i]['tableName'] = ggdm30.rules.thematicGroupList[gFcode];
+            ggdm30.validateTDSAttrs(gFcode, returnData[i]['attrs']);
+          }
+          else
+          {
+            returnData[i]['tableName'] = layerNameLookup[gFcode.toUpperCase()];
+          }
         }
-
-        if (ggdm30.lookup == undefined)
+        else
         {
-            // Add "other" rules to the one2one
-            ggdm30.rules.one2one.push.apply(ggdm30.rules.one2one,ggdm30.rules.one2oneOut);
-
-            ggdm30.lookup = translate.createBackwardsLookup(ggdm30.rules.one2one);
-            // Debug
-            //translate.dumpOne2OneLookup(ggdm30.lookup);
-
-            // Make the fuzzy lookup table
-            ggdm30.fuzzy = schemaTools.generateToOgrTable(ggdm30.rules.fuzzyTable);
-
-            // Debug
-//             for (var k1 in ggdm30.fuzzy)
-//             {
-//                 for (var v1 in ggdm30.fuzzy[k1])
-//                 {
-//                     print(JSON.stringify([k1, v1, ggdm30.fuzzy[k1][v1][0], ggdm30.fuzzy[k1][v1][1], ggdm30.fuzzy[k1][v1][2]]));
-//                 }
-//             }
-        } // End ggdm30.lookup Undefined
-
-        // Override values if appropriate
-        translate.overrideValues(tags,ggdm30.toChange);
-
-        // Pre Processing
-        ggdm30.applyToOgrPreProcessing(tags, attrs, geometryType);
-
-        // Make a copy of the input tags so we can remove them as they get translated. What is left is
-        // the not used tags
-        // not in v8 yet: // var tTags = Object.assign({},tags);
-        var notUsedTags = (JSON.parse(JSON.stringify(tags)));
-
-        if (notUsedTags.hoot) delete notUsedTags.hoot; // Added by the UI
-        // Debug info. We use this in postprocessing via "tags"
-        if (notUsedTags['hoot:id']) delete notUsedTags['hoot:id'];
-
-        // Apply the simple number and text biased rules
-        // NOTE: These are BACKWARD, not forward!
-        // NOTE: These delete tags as they are used
-        translate.applySimpleNumBiased(attrs, notUsedTags, ggdm30.rules.numBiased,'backward',ggdm30.rules.intList);
-        translate.applySimpleTxtBiased(attrs, notUsedTags, ggdm30.rules.txtBiased,'backward');
-
-        // Apply the fuzzy rules
-        // NOTE: This deletes tags as they are used
-        translate.applyOne2OneQuiet(notUsedTags, attrs, ggdm30.fuzzy);
-
-        // Translate the XXX:2, XXX2, XXX:3 etc attributes
-        // Note: This deletes tags as they are used
-        translate.fix23Tags(notUsedTags, attrs, ggdm30.lookup);
-
-        // one 2 one: we call the version that knows about the OTH field
-        // NOTE: This deletes tags as they are used
-        translate.applyTdsOne2One(notUsedTags, attrs, ggdm30.lookup, ggdm30.fcodeLookup);
-
-        // Post Processing
-        // We send the original list of tags and the list of tags we haven't used yet
-        // ggdm30.applyToOgrPostProcessing(tags, attrs, geometryType);
-        ggdm30.applyToOgrPostProcessing(tags, attrs, geometryType, notUsedTags);
-
-        // Debug
-        if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(notUsedTags,'',geometryType,elementType,'Not used: ');
-
-        // If we have unused tags, add them to the memo field
-        if (Object.keys(notUsedTags).length > 0 && ggdm30.config.OgrNoteExtra == 'attribute')
+          // If the feature is not valid, just drop it
+          // Debug
+          // print('## Skipping: ' + gFcode);
+          returnData.splice(i,1);
+        }
+      } // End returnData loop
+
+      // If we have unused tags, throw them into the "extra" layer
+      if (Object.keys(notUsedTags).length > 0 && ggdm30.config.OgrNoteExtra == 'file')
+      {
+        var extraFeature = {};
+        extraFeature.tags = JSON.stringify(notUsedTags);
+        extraFeature.uuid = attrs.UID;
+
+        var extraName = 'extra_' + geometryType.toString().charAt(0);
+
+        returnData.push({attrs: extraFeature, tableName: extraName});
+      } // End notUsedTags
+
+      // Look for Review tags and push them to a review layer if found
+      if (tags['hoot:review:needs'] == 'yes')
+      {
+        var reviewAttrs = {};
+
+        // Note: Some of these may be "undefined"
+        reviewAttrs.note = tags['hoot:review:note'];
+        reviewAttrs.score = tags['hoot:review:score'];
+        reviewAttrs.uuid = tags.uuid;
+        reviewAttrs.source = tags['hoot:review:source'];
+
+        var reviewTable = 'review_' + geometryType.toString().charAt(0);
+        returnData.push({attrs: reviewAttrs, tableName: reviewTable});
+      } // End ReviewTags
+    }
+    else
+    {
+      // For the UI: Throw an error and die if we don't have a valid feature
+      if (ggdm30.config.OgrThrowError == 'true')
+      {
+        if (! attrs.F_CODE)
         {
-            var tStr = '<OSM>' + JSON.stringify(notUsedTags) + '</OSM>';
-            attrs.ZI006_MEM = translate.appendValue(attrs.ZI006_MEM,tStr,';');
+          returnData.push({attrs:{'error':'No Valid Feature Code'}, tableName: ''});
+          return returnData;
         }
-
-        // Now check for invalid feature geometry
-        // E.g. If the spec says a runway is a polygon and we have a line, throw error and
-        // push the feature to o2s layer
-        var gFcode = geometryType.toString().charAt(0) + attrs.F_CODE;
-
-        if (ggdmAttrLookup[gFcode])
+        else
         {
-            // Check if we need to make more features
-            // NOTE: This returns structure we are going to send back to Hoot:  {attrs: attrs, tableName: 'Name'}
-            returnData = ggdm30.manyFeatures(geometryType,tags,attrs);
-
-            // Debug: Add the first feature
-            //returnData.push({attrs: attrs, tableName: ''});
-
-            // Now go through the features and clean them up
-            var gType = geometryType.toString().charAt(0);
-            for (var i = 0, fLen = returnData.length; i < fLen; i++)
-            {
-                var gFcode = gType + returnData[i]['attrs']['F_CODE'];
-
-                if (ggdmAttrLookup[gFcode.toUpperCase()])
-                {
-                    // Validate attrs: remove all that are not supposed to be part of a feature
-                    ggdm30.validateAttrs(geometryType,returnData[i]['attrs']);
-
-                    // Now set the FCSubtype
-                    // NOTE: If we export to shapefile, GAIT _will_ complain about this
-                    if (ggdm30.config.OgrEsriFcsubtype == 'true')
-                    {
-                        returnData[i]['attrs']['FCSUBTYPE'] = ggdm30.rules.subtypeList[returnData[i]['attrs']['F_CODE']];
-                    }
+          //throw new Error(geometryType.toString() + ' geometry is not valid for F_CODE ' + attrs.F_CODE);
+          returnData.push({attrs:{'error':geometryType + ' geometry is not valid for ' + attrs.F_CODE + ' in TDSv61'}, tableName: ''});
+          return returnData;
+        }
+      }
 
-                    // If we are using the Thematic structure, fill the rest of the unused attrs in the schema
-                    if (ggdm30.config.OgrThematicStructure == 'true')
-                    {
-                        returnData[i]['tableName'] = ggdm30.rules.thematicGroupList[gFcode];
-                        ggdm30.validateTDSAttrs(gFcode, returnData[i]['attrs']);
-                    }
-                    else
-                    {
-                        returnData[i]['tableName'] = layerNameLookup[gFcode.toUpperCase()];
-                    }
-                }
-                else
-                {
-                    // If the feature is not valid, just drop it
-                    // Debug
-                    // print('## Skipping: ' + gFcode);
-                    returnData.splice(i,1);
-                }
-            } // End returnData loop
-
-            // If we have unused tags, throw them into the "extra" layer
-            if (Object.keys(notUsedTags).length > 0 && ggdm30.config.OgrNoteExtra == 'file')
-            {
-                var extraFeature = {};
-                extraFeature.tags = JSON.stringify(notUsedTags);
-                extraFeature.uuid = attrs.UID;
+      hoot.logTrace('FCODE and Geometry: ' + gFcode + ' is not in the schema');
 
-                var extraName = 'extra_' + geometryType.toString().charAt(0);
+      tableName = 'o2s_' + geometryType.toString().charAt(0);
 
-                returnData.push({attrs: extraFeature, tableName: extraName});
-            } // End notUsedTags
+      // Debug:
+      // Dump out what attributes we have converted before they get wiped out
+      if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(attrs,'',geometryType,elementType,'Converted attrs: ');
 
-            // Look for Review tags and push them to a review layer if found
-            if (tags['hoot:review:needs'] == 'yes')
-            {
-                var reviewAttrs = {};
+      // We want to keep the hoot:id if present
+      if (tags['hoot:id'])
+      {
+        tags.raw_id = tags['hoot:id'];
+        delete tags['hoot:id'];
+      }
 
-                // Note: Some of these may be "undefined"
-                reviewAttrs.note = tags['hoot:review:note'];
-                reviewAttrs.score = tags['hoot:review:score'];
-                reviewAttrs.uuid = tags.uuid;
-                reviewAttrs.source = tags['hoot:review:source'];
+      // Convert all of the Tags to a string so we can jam it into an attribute
+      // var str = JSON.stringify(tags);
+      var str = JSON.stringify(tags,Object.keys(tags).sort());
 
-                var reviewTable = 'review_' + geometryType.toString().charAt(0);
-                returnData.push({attrs: reviewAttrs, tableName: reviewTable});
-            } // End ReviewTags
-        }
-        else
+      // Shapefiles can't handle fields > 254 chars
+      // If the tags are > 254 char, split into pieces. Not pretty but stops errors
+      // A nicer thing would be to arrange the tags until they fit neatly
+      if (str.length < 255 || ggdm30.config.OgrSplitO2s == 'false')
+      {
+        // return {attrs:{tag1:str}, tableName: tableName};
+        attrs = {tag1:str};
+      }
+      else
+      {
+        // Not good. Will fix with the rewrite of the tag splitting code
+        if (str.length > 1012)
         {
-            // For the UI: Throw an error and die if we don't have a valid feature
-            if (ggdm30.config.OgrThrowError == 'true')
-            {
-                if (! attrs.F_CODE)
-                {
-                    returnData.push({attrs:{'error':'No Valid Feature Code'}, tableName: ''});
-                    return returnData;
-                }
-                else
-                {
-                    //throw new Error(geometryType.toString() + ' geometry is not valid for F_CODE ' + attrs.F_CODE);
-                    returnData.push({attrs:{'error':geometryType + ' geometry is not valid for ' + attrs.F_CODE + ' in TDSv61'}, tableName: ''});
-                    return returnData;
-                }
-            }
-
-            hoot.logTrace('FCODE and Geometry: ' + gFcode + ' is not in the schema');
-
-            tableName = 'o2s_' + geometryType.toString().charAt(0);
-
-            // Debug:
-            // Dump out what attributes we have converted before they get wiped out
-            if (ggdm30.config.OgrDebugDumptags == 'true') translate.debugOutput(attrs,'',geometryType,elementType,'Converted attrs: ');
-
-            // We want to keep the hoot:id if present
-            if (tags['hoot:id'])
-            {
-                tags.raw_id = tags['hoot:id'];
-                delete tags['hoot:id'];
-            }
-
-            // Convert all of the Tags to a string so we can jam it into an attribute
-            // var str = JSON.stringify(tags);
-            var str = JSON.stringify(tags,Object.keys(tags).sort());
+          hoot.logTrace('o2s tags truncated to fit in available space.');
+          str = str.substring(0,1012);
+        }
 
-            // Shapefiles can't handle fields > 254 chars
-            // If the tags are > 254 char, split into pieces. Not pretty but stops errors
-            // A nicer thing would be to arrange the tags until they fit neatly
-            if (str.length < 255 || ggdm30.config.OgrSplitO2s == 'false')
-            {
-                // return {attrs:{tag1:str}, tableName: tableName};
-                attrs = {tag1:str};
-            }
-            else
-            {
-                // Not good. Will fix with the rewrite of the tag splitting code
-                if (str.length > 1012)
-                {
-                    hoot.logTrace('o2s tags truncated to fit in available space.');
-                    str = str.substring(0,1012);
-                }
-
-                // return {attrs:{tag1:str.substring(0,253), tag2:str.substring(253)}, tableName: tableName};
-                attrs = {tag1:str.substring(0,253),
-                         tag2:str.substring(253,506),
-                         tag3:str.substring(506,759),
-                         tag4:str.substring(759,1012)};
-            }
+        // return {attrs:{tag1:str.substring(0,253), tag2:str.substring(253)}, tableName: tableName};
+        attrs = {tag1:str.substring(0,253),
+          tag2:str.substring(253,506),
+          tag3:str.substring(506,759),
+          tag4:str.substring(759,1012)};
+      }
 
-            returnData.push({attrs: attrs, tableName: tableName});
-        } // End We DON'T have a feature
+      returnData.push({attrs: attrs, tableName: tableName});
+    } // End We DON'T have a feature
 
-        // Debug:
-        if (ggdm30.config.OgrDebugDumptags == 'true')
-        {
-            for (var i = 0, fLen = returnData.length; i < fLen; i++)
-            {
-                print('TableName ' + i + ': ' + returnData[i]['tableName'] + '  FCode: ' + returnData[i]['attrs']['F_CODE'] + '  Geom: ' + geometryType);
-                translate.debugOutput(returnData[i]['attrs'],'',geometryType,elementType,'Out attrs: ');
-            }
-            print('');
-        }
+    // Debug:
+    if (ggdm30.config.OgrDebugDumptags == 'true')
+    {
+      for (var i = 0, fLen = returnData.length; i < fLen; i++)
+      {
+        print('TableName ' + i + ': ' + returnData[i]['tableName'] + '  FCode: ' + returnData[i]['attrs']['F_CODE'] + '  Geom: ' + geometryType);
+        translate.debugOutput(returnData[i]['attrs'],'',geometryType,elementType,'Out attrs: ');
+      }
+      print('');
+    }
 
-        return returnData;
+    return returnData;
 
-    } // End of toOgr
+  } // End of toOgr
 
-} // End of ggdm30
+}; // End of ggdm30
Clone this wiki locally