Skip to content

v0.2.54..v0.2.55 changeset mgcp.js

Garret Voltz edited this page Aug 14, 2020 · 1 revision
diff --git a/translations/mgcp.js b/translations/mgcp.js
index 2be5b9c..95011db 100644
--- a/translations/mgcp.js
+++ b/translations/mgcp.js
@@ -26,2352 +26,2368 @@
  */
 
 /*
-    MGCP conversion script for TRD3 and TRD4
-        MGCP [TRD3 | TRD4] -> OSM, and
-        OSM -> MGCP TRD4
+  MGCP conversion script for TRD3 and TRD4
+    MGCP [TRD3 | TRD4] -> OSM, and
+    OSM -> MGCP TRD4
 
-    Based on TableExample.js script by Jason S.
+  Based on TableExample.js script by Jason S.
 
-    Possible attribute values are taken from the MGCP TRD 3.0 and 4.0 specs  with the addition of
-    values found in sample data and the DFDD/NFDD v4
+  Possible attribute values are taken from the MGCP TRD 3.0 and 4.0 specs  with the addition of
+  values found in sample data and the DFDD/NFDD v4
 */
 
 
 mgcp = {
-    getDbSchema: function() {
+  getDbSchema: function() {
 
-    mgcp.rawSchema = mgcp.schema.getDbSchema(); // This is <GLOBAL> so we can access it from other areas
+  mgcp.rawSchema = mgcp.schema.getDbSchema(); // This is <GLOBAL> so we can access it from other areas
 
-    // Build the MGCP fcode/attrs lookup table. Note: This is <GLOBAL>
-    mgcp.AttrLookup = translate.makeAttrLookup(mgcp.rawSchema);
+  // Build the MGCP fcode/attrs lookup table. Note: This is <GLOBAL>
+  mgcp.AttrLookup = translate.makeAttrLookup(mgcp.rawSchema);
 
-    // Now build the FCODE/layername lookup table. Note: This is <GLOBAL>
-    mgcp.layerNameLookup = translate.makeLayerNameLookup(mgcp.rawSchema);
+  // Now build the FCODE/layername lookup table. Note: This is <GLOBAL>
+  mgcp.layerNameLookup = translate.makeLayerNameLookup(mgcp.rawSchema);
 
-    // Now add an o2s[A,L,P] feature to the mgcp.rawSchema
-    // We can drop features but this is a nice way to see what we would drop
-    mgcp.rawSchema = translate.addEmptyFeature(mgcp.rawSchema);
+  // Now add an o2s[A,L,P] feature to the mgcp.rawSchema
+  // We can drop features but this is a nice way to see what we would drop
+  mgcp.rawSchema = translate.addEmptyFeature(mgcp.rawSchema);
 
-    // Add empty Review layers
-    mgcp.rawSchema = translate.addReviewFeature(mgcp.rawSchema);
+  // Add empty Review layers
+  mgcp.rawSchema = translate.addReviewFeature(mgcp.rawSchema);
 
-    // Add empty "extra" feature layers if needed
-    if (config.getOgrNoteExtra() == 'file') mgcp.rawSchema = translate.addExtraFeature(mgcp.rawSchema);
+  // Add empty "extra" feature layers if needed
+  if (config.getOgrNoteExtra() == 'file') mgcp.rawSchema = translate.addExtraFeature(mgcp.rawSchema);
 
-    // This function dumps the schema to the screen for debugging
-    // translate.dumpSchema(mgcp.rawSchema);
+  // This function dumps the schema to the screen for debugging
+  // translate.dumpSchema(mgcp.rawSchema);
 
-    return mgcp.rawSchema;
-    }, // End of getDbSchema
+  return mgcp.rawSchema;
+  }, // End of getDbSchema
 
 
-    // validateAttrs: Clean up the supplied attr list by dropping anything that should not be part of the
-    //                feature
-    validateAttrs: function(geometryType,attrs) {
+  // validateAttrs: Clean up the supplied attr list by dropping anything that should not be part of the
+  //        feature
+  validateAttrs: function(geometryType,attrs) {
+    var attrList = mgcp.AttrLookup[geometryType.toString().charAt(0) + attrs.FCODE];
 
-        var attrList = mgcp.AttrLookup[geometryType.toString().charAt(0) + attrs.FCODE];
-
-        if (attrList != undefined)
-        {
-            for (var val in attrs)
-            {
-                if (attrList.indexOf(val) == -1)
-                {
-                    hoot.logDebug('Validate: Dropping ' + val + ' from ' + attrs.FCODE);
-                    delete attrs[val];
-
-                    // Since we deleted the attribute, Skip the text check
-                    continue;
-                }
-
-                // 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 mgcp.rules.txtLength)
-                {
-                    if (attrs[val].length > mgcp.rules.txtLength[val])
-                    {
-                        // First try splitting the attribute and grabbing the first value
-                        var tStr = attrs[val].split(';');
-                        if (tStr[0].length <= mgcp.rules.txtLength[val])
-                        {
-                            attrs[val] = tStr[0];
-                        }
-                        else
-                        {
-                            hoot.logDebug('Validate: Attribute ' + val + ' is ' + attrs[val].length + ' characters long. Truncating to ' + mgcp.rules.txtLength[val] + ' characters.');
-                            // Still too long. Chop to the maximum length
-                            attrs[val] = tStr[0].substring(0,mgcp.rules.txtLength[val]);
-                        }
-                    } // End text attr length > max length
-                } // End in txtLength
-            }
-        }
-        else
+    if (attrList != undefined)
+    {
+      for (var val in attrs)
+      {
+        if (attrList.indexOf(val) == -1)
         {
-            hoot.logDebug('Validate: No attrList for ' + attrs.FCODE + ' ' + geometryType);
-        }
+          hoot.logDebug('Validate: Dropping ' + val + ' from ' + attrs.FCODE);
+          delete attrs[val];
 
-        // No quick and easy way to do this unless we build yet another lookup table
-        var feature = {};
+          // Since we deleted the attribute, Skip the text check
+          continue;
+        }
 
-        for (var i=0, sLen = mgcp.rawSchema.length; i < sLen; i++)
+        // 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 mgcp.rules.txtLength)
         {
-            if (mgcp.rawSchema[i].fcode == attrs.FCODE && mgcp.rawSchema[i].geom == geometryType)
+          if (attrs[val].length > mgcp.rules.txtLength[val])
+          {
+            // First try splitting the attribute and grabbing the first value
+            var tStr = attrs[val].split(';');
+            if (tStr[0].length <= mgcp.rules.txtLength[val])
             {
-                feature = mgcp.rawSchema[i];
-                break;
+              attrs[val] = tStr[0];
             }
-        }
-
-        // 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 enumName = feature.columns[i].name;
-
-            // Skip stuff that is missing and will end up as a default value
-            if (!attrs[enumName]) continue;
-
-            var attrValue = attrs[enumName];
-            var enumList = feature.columns[i].enumerations;
-            var enumValueList = [];
-
-            // 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);
-
-            // Check if it is a valid enumerated value
-            if (enumValueList.indexOf(attrValue) == -1)
+            else
             {
-                hoot.logDebug('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName);
-
-                // Do we have an "Other" value?
-                if (enumValueList.indexOf('999') == -1)
-                {
-                    // No: Set the offending enumerated value to the default value
-                    attrs[enumName] = feature.columns[i].defValue;
-
-                    hoot.logDebug('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting ' + enumName + ' to its default value (' + feature.columns[i].defValue + ')');
-                }
-                else
-                {
-                    // Yes: Set the offending enumerated value to the "other" value
-                    attrs[enumName] = '999';
-
-                    hoot.logDebug('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting ' + enumName + ' to Other (999)');
-                }
+              hoot.logDebug('Validate: Attribute ' + val + ' is ' + attrs[val].length + ' characters long. Truncating to ' + mgcp.rules.txtLength[val] + ' characters.');
+              // Still too long. Chop to the maximum length
+              attrs[val] = tStr[0].substring(0,mgcp.rules.txtLength[val]);
             }
-        } // End Validate Enumerations
-    }, // End validateAttrs
+          } // End text attr length > max length
+        } // End in txtLength
+      }
+    }
+    else
+    {
+      hoot.logDebug('Validate: No attrList for ' + attrs.FCODE + ' ' + geometryType);
+    }
 
+    // No quick and easy way to do this unless we build yet another lookup table
+    var feature = {};
 
-    // 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)
+    for (var i=0, sLen = mgcp.rawSchema.length; i < sLen; i++)
+    {
+      if (mgcp.rawSchema[i].fcode == attrs.FCODE && mgcp.rawSchema[i].geom == geometryType)
+      {
+        feature = mgcp.rawSchema[i];
+        break;
+      }
+    }
+
+    // Now validate the Enumerated values
+    for (var i=0, cLen = feature['columns'].length; i < cLen; i++)
     {
-        // Add the first feature to the structure that we return
-        var returnData = [{attrs:attrs, tableName:''}];
+      // Skip non enumeratied attributes
+      if (feature.columns[i].type !== 'enumeration') continue;
 
-        // Quit early if we don't need to check anything. We are only looking at linework
-        if (geometryType !== 'Line') return returnData;
+      var enumName = feature.columns[i].name;
 
-        // Only looking at roads & railways with something else tacked on
-        if (!(tags.highway || tags.railway)) return returnData;
+      // Skip stuff that is missing and will end up as a default value
+      if (!attrs[enumName]) continue;
 
-        // Check the list of secondary/tertiary etc features
-        if (!(tags.bridge || tags.tunnel || tags.embankment || tags.cutting || tags.ford)) return returnData;
+      var attrValue = attrs[enumName];
+      var enumList = feature.columns[i].enumerations;
+      var enumValueList = [];
 
-        // 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'];
+      // 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);
 
-        // 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;
-                break;
-
-            case 'AQ130': // Tunnel
-                delete nTags.tunnel;
-                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;
-        }
+      // Check if it is a valid enumerated value
+      if (enumValueList.indexOf(attrValue) == -1)
+      {
+        hoot.logDebug('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName);
 
-        if (nTags.highway)
+        // Do we have an "Other" value?
+        if (enumValueList.indexOf('999') == -1)
         {
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.highway;
-        }
+          // No: Set the offending enumerated value to the default value
+          attrs[enumName] = feature.columns[i].defValue;
 
-        if (nTags.cutting)
-        {
-            newAttributes.F_CODE = 'DB070';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.cutting;
+          hoot.logDebug('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting ' + enumName + ' to its default value (' + feature.columns[i].defValue + ')');
         }
-
-        if (nTags.bridge && nTags.bridge !== 'no')
+        else
         {
-            newAttributes.F_CODE = 'AQ040';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.bridge;
-        }
+          // Yes: Set the offending enumerated value to the "other" value
+          attrs[enumName] = '999';
 
-        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;
+          hoot.logDebug('Validate: Enumerated Value: ' + attrValue + ' not found in ' + enumName + ' Setting ' + enumName + ' to Other (999)');
         }
+      }
+    } // End Validate Enumerations
+  }, // End validateAttrs
 
-        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;
-        }
 
-        if (nTags.embankment)
-        {
-            newAttributes.F_CODE = 'DB090';
-            newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
-            delete nTags.embankment;
-        }
+  // 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)
+  {
+    // Add the first feature to the structure that we return
+    var returnData = [{attrs:attrs, tableName:''}];
 
-        // Loop through the new features and process them
-        for (var i = 0, nFeat = newFeatures.length; i < nFeat; i++)
-        {
-            // pre processing
-            mgcp.applyToMgcpPreProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType);
+    // Quit early if we don't need to check anything. We are only looking at linework
+    if (geometryType !== 'Line') return returnData;
 
-            var notUsedTags = (JSON.parse(JSON.stringify(tags)));
+    // Only looking at roads & railways with something else tacked on
+    if (!(tags.highway || tags.railway)) return returnData;
 
-            // apply the simple number and text biased rules
-            // Note: These are BACKWARD, not forward!
-            translate.applySimpleNumBiased(newFeatures[i]['attrs'], notUsedTags, mgcp.rules.numBiased,'backward',mgcp.rules.intList);
-            translate.applySimpleTxtBiased(newFeatures[i]['attrs'], notUsedTags, mgcp.rules.txtBiased,'backward');
+    // Check the list of secondary/tertiary etc features
+    if (!(tags.bridge || tags.tunnel || tags.embankment || tags.cutting || tags.ford)) return returnData;
 
-            // one 2 one - we call the version that knows about OTH fields
-            translate.applyOne2One(notUsedTags, newFeatures[i]['attrs'], mgcp.lookup, mgcp.fcodeLookup);
+    // 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'];
 
-            // post processing
-            mgcp.applyToMgcpPostProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType,notUsedTags);
+    // 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;
+        break;
+
+      case 'AQ130': // Tunnel
+        delete nTags.tunnel;
+        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
 
-            returnData.push({attrs: newFeatures[i]['attrs'],tableName: ''});
-        }
+    // 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;
+    }
 
-        return returnData;
-    }, // End manyFeatures
+    if (nTags.highway)
+    {
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.highway;
+    }
 
+    if (nTags.cutting)
+    {
+      newAttributes.F_CODE = 'DB070';
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.cutting;
+    }
 
-    // ##### Start of the xxToOsmxx Block #####
+    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;
+    }
 
-    // Untangle MGCP attributes & OSM tags
-    // Some people have been editing OSM files and inserting MGCP attributes
-    untangleAttributes: function (attrs, tags)
+    if (nTags.tunnel && nTags.tunnel !== 'no')
     {
-        // If we use ogr2osm, the GDAL driver jams any tag it doesn't know about into an "other_tags" tag
-        // We need to unpack this before we can do anything
-        if (attrs.other_tags)
-        {
-            var tList = attrs.other_tags.split('","');
+      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;
+    }
 
-            delete attrs.other_tags;
+    if (nTags.embankment)
+    {
+      newAttributes.F_CODE = 'DB090';
+      newFeatures.push({attrs: JSON.parse(JSON.stringify(newAttributes)), tags: JSON.parse(JSON.stringify(nTags))});
+      delete nTags.embankment;
+    }
 
-            for (var val in tList)
-            {
-                vList = tList[val].split('"=>"');
+    // Loop through the new features and process them
+    for (var i = 0, nFeat = newFeatures.length; i < nFeat; i++)
+    {
+      // pre processing
+      mgcp.applyToMgcpPreProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType);
 
-                attrs[vList[0].replace('"','')] = vList[1].replace('"','');
+      var notUsedTags = (JSON.parse(JSON.stringify(tags)));
 
-                // Debug
-                //print('val: ' + tList[val] + '  vList[0] = ' + vList[0] + '  vList[1] = ' + vList[1]);
-            }
-        }
+      // apply the simple number and text biased rules
+      // Note: These are BACKWARD, not forward!
+      translate.numToOgr(newFeatures[i]['attrs'], notUsedTags, mgcp.rules.numBiased,mgcp.rules.intList,transMap);
+      translate.txtToOgr(newFeatures[i]['attrs'], notUsedTags, mgcp.rules.txtBiased,transMap);
 
-        for (var col in attrs)
-        {
-            // Sort out FCODE funkyness:  f_CODE, F_Code etc
-            var tKey = col.toLowerCase();
-            tKey = tKey.replace(/\s/g, '').replace(/_/g, '');;
+      // one 2 one - we call the version that knows about OTH fields
+      translate.applyOne2One(notUsedTags, newFeatures[i]['attrs'], mgcp.lookup, mgcp.fcodeLookup, transMap);
 
-            if (tKey == 'fcode' && col !== 'F_CODE')
-            {
-                attrs.F_CODE = attrs[col];
-                delete attrs[col];
-                continue;
-            }
+      // post processing
+      mgcp.applyToMgcpPostProcessing(newFeatures[i]['tags'], newFeatures[i]['attrs'], geometryType,notUsedTags);
 
-            // Check for an FCODE as a tag
-            if (col in mgcp.fcodeLookup['F_CODE'])
-            {
-                attrs.F_CODE = col;
-                delete attrs[col];
-                continue;
-            }
+      returnData.push({attrs: newFeatures[i]['attrs'],tableName: ''});
+    }
 
-            // Stuff to be ignored or that gets swapped later - See applyToOsmPreProcessing
-            if (['FCODE','error_circ','CPYRT_NOTE','SRC_INFO','SRC_DATE','SMC'].indexOf(col) > -1) continue;
+    return returnData;
+  }, // End manyFeatures
 
-            // Look for Attributes
-            if (col in mgcp.numLookup) continue;
 
-            if (col in mgcp.txtLookup) continue;
+  // ##### Start of the xxToOsmxx Block #####
 
-            if (col in mgcp.lookup) continue;
+  // Untangle MGCP attributes & OSM tags
+  // Some people have been editing OSM files and inserting MGCP attributes
+  untangleAttributes: function (attrs, tags)
+  {
+    // If we use ogr2osm, the GDAL driver jams any tag it doesn't know about into an "other_tags" tag
+    // We need to unpack this before we can do anything
+    if (attrs.other_tags)
+    {
+      var tList = attrs.other_tags.split('","');
 
-            // Drop the "GEOM" attribute
-            if (col == 'GEOM')
-            {
-                delete attrs[col];
-                continue;
-            }
+      delete attrs.other_tags;
 
-            // Not an Attribute so push it to the tags object
-            tags[col] = attrs[col];
-            delete attrs[col];
-        }
+      for (var val in tList)
+      {
+        vList = tList[val].split('"=>"');
 
-    }, // End attribute attributeUntangle
+        attrs[vList[0].replace('"','')] = vList[1].replace('"','');
 
+        // Debug
+        //print('val: ' + tList[val] + '  vList[0] = ' + vList[0] + '  vList[1] = ' + vList[1]);
+      }
+    }
 
-    applyToOsmPreProcessing: function(attrs, layerName, geometryType)
+    for (var col in attrs)
+    {
+      // Sort out FCODE funkyness:  f_CODE, F_Code etc
+      var tKey = col.toLowerCase();
+      tKey = tKey.replace(/\s/g, '').replace(/_/g, '');;
+
+      if (tKey == 'fcode' && col !== 'F_CODE')
+      {
+        attrs.F_CODE = attrs[col];
+        delete attrs[col];
+        continue;
+      }
+
+      // Check for an FCODE as a tag
+      if (col in mgcp.fcodeLookup['F_CODE'])
+      {
+        attrs.F_CODE = col;
+        delete attrs[col];
+        continue;
+      }
+
+      // Stuff to be ignored or that gets swapped later - See applyToOsmPreProcessing
+      if (['FCODE','error_circ','CPYRT_NOTE','SRC_INFO','SRC_DATE','SMC'].indexOf(col) > -1) continue;
+
+      // Look for Attributes
+      if (col in mgcp.numLookup) continue;
+
+      if (col in mgcp.txtLookup) continue;
+
+      if (col in mgcp.lookup) continue;
+
+      // Drop the "GEOM" attribute
+      if (col == 'GEOM')
+      {
+        delete attrs[col];
+        continue;
+      }
+
+      // Not an Attribute so push it to the tags object
+      tags[col] = attrs[col];
+      delete attrs[col];
+    }
+
+  }, // End attribute attributeUntangle
+
+
+  applyToOsmPreProcessing: function(attrs, layerName, geometryType)
+  {
+    // Drop the FCSUBTYPE since we don't use it
+    if (attrs.FCSUBTYPE) delete attrs.FCSUBTYPE;
+
+    // The swap list. These are the same attr, just named differently
+    // These may get converted back on output
+    var swapList = {
+        'CPYRT_NOTE':'CCN',
+        'SRC_INFO':'SDP',
+        'SRC_DATE':'SDV',
+        'SMC':'MCC'
+        };
+
+    // List of data values to drop/ignore
+    var ignoreList = { '-32765':1,'-32767':1,'-32768':1,
+               '998':1,'-999999':1,
+               'n_a':1,'noinformation':1,'unknown':1,'unk':1 };
+
+    // Unit conversion. Some attributes are in centimetres, others in decimetres
+    // var unitList = { 'GAW':100,'HCA':10,'WD1':10,'WD2':10,'WD3':10,'WT2':10 };
+
+    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] == '-32768' || attrValue == 'unk' || attrValue == 'n_a' || attrValue == 'noinformation' || attrs[col] == '')
+      if (attrs[col] == '' || attrValue in ignoreList || attrs[col] in ignoreList)
+      {
+        // debug: Comment this out to leave all of the No Info stuff in for testing
+        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
+      }
+
+      // Sort out units - if needed
+      // if (col in unitList) attrs[col] = attrs[col] / unitList[col];
+
+      // Now see if we need to swap attr names
+      if (col in swapList)
+      {
+        // print('Swapped: ' + swapList[i]); // debug
+        attrs[swapList[col]] = attrs[col];
+        delete attrs[col];
+        continue
+      }
+    } // End col in attrs loop
+
+    if (attrs.F_CODE)
+    {
+      // Drop the the "Not Found" F_CODE. This is from the UI
+      // NOTE: We _should_ be getting "FCODE" not "F_CODE" from files/UI
+      if (attrs.F_CODE == 'Not found') delete attrs.F_CODE;
+    }
+    else if (attrs.FCODE)
+    {
+      // Swap these since the rest of the lookup tables & TDS use F_CODE
+      if (attrs.FCODE !== 'Not found') attrs.F_CODE = attrs.FCODE;
+
+      delete attrs.FCODE;
+    }
+    else
+    {
+      // Time to find an FCODE based on the filename
+      var fCodeMap = [
+        ['AA010', ['aa010']], // Extraction Mine
+        ['AA012', ['aa012']], // Quarry
+        ['AA040', ['aa040']], // Rig
+        ['AA050', ['aa050','well_p']], // Well
+        ['AC000', ['ac000']], // Processing Facility TRDv3 only
+        ['AC020', ['ac020']], // Catalytic Cracker
+        ['AD030', ['ad030']], // Power Substation
+        ['AF010', ['af010']], // Smokestack
+        ['AF030', ['af030']], // Cooling Tower: TRDv30 and TDS but not TRDv4
+        ['AF040', ['af040']], // Crane
+        ['AF070', ['af070']], // Flare Pipe
+        ['AH050', ['ah050']], // Fortification
+        ['AH070', ['ah070']], // Check Point
+        ['AJ051', ['aj051']], // Wind Turbine
+        ['AK040', ['ak040']], // Sports Ground
+        ['AK160', ['ak160']], // Stadium
+        ['AL015', ['al015','building']], // Building
+        ['AL020', ['al020']], // Built up area
+        ['AL025', ['al025']], // Cairn
+        ['AL030', ['al030']], // Cemetary
+        ['AL080', ['al080']], // Gantry
+        ['AL100', ['al100']], // Hut
+        ['AL105', ['al105']], // Settlement
+        ['AL130', ['al130']], // Memorial Monument
+        ['AL200', ['al200']], // Ruins
+        ['AL240', ['al240']], // Non-communication Tower - TRD30
+        ['AL241', ['al241']], // Tower - TRD40
+        ['AM020', ['am020']], // Grain Storage Structure
+        ['AM030', ['am030']], // Grain Elevator
+        ['AM040', ['am040']], // Mineral Pile
+        ['AM060', ['am060']], // Surface Bunker
+        ['AM070', ['am070']], // Storage Tank
+        ['AM070', ['am080']], // Water Tower
+        ['AN010', ['an010','railroad_l']], // Railway
+        ['AN050', ['an050']], // Railway Sidetrack
+        ['AN060', ['an060']], // Railway Yard
+        ['AN075', ['an075']], // Railway Turntable
+        ['AP010', ['ap010','trackl','track_l','cart_track','carttrack','cart_track_l']], // Cart Track
+        ['AP020', ['ap020','interchange_l','interchangel']], // Interchange
+        ['AP030', ['ap030','roadnet','road_l','roadl']], // Road
+        ['AP050', ['ap050','traill','trail_l']], // Trail
+        ['AQ040', ['aq040','bridgel','bridge_l','bridge_tunnel_l','bridge_overpass_l']], // Bridge
+        ['AQ060', ['aq060']], // Control Tower
+        ['AQ065', ['aq065']], // Culvert
+        ['AQ090', ['aq090']], // Entrance and/or Exit
+        ['AQ113', ['aq113']], // Pipeline
+        ['AQ116', ['aq116']], // Pumping Station
+        ['AQ125', ['aq125']], // Transportation Station
+        ['AQ130', ['aq130','tunnell','tunnel_l']], // Tunnel
+        ['AT010', ['at010']], // Dish Aerial
+        ['AT042', ['at042']], // Pylon
+        ['AT045', ['at045']], // Radar Station
+        // ['AT050', ['at050']], // Communication Station
+        ['AT080', ['at080']], // Communication Tower
+        ['BH010', ['bh010']], // Aqueduct
+        ['BH020', ['bh020']], // Canal
+        ['BH030', ['bh030']], // Ditch
+        ['BH070', ['bh070']], // Ford
+        ['BH140', ['bh140','river_stream_l']], // River
+        ['BH145', ['bh145']], // Vanishing Point
+        ['BH170', ['bh170']], // Natural Pool
+        ['BI010', ['bi010']], // Cistern
+        ['BI020', ['bi020']], // Dam
+        ['BI040', ['bi040']], // Sluice Gate
+        ['BI050', ['bi050']], // Water Intake Tower
+        ['DB030', ['db030']], // Cave
+        ['DB070', ['db070']], // Cut
+        ['DB150', ['db150']], // Mountain Pass
+        ['GA035', ['ga035']], // Aeronautical NAVAID - TRDv30
+        ['GB010', ['gb010']], // Airport Navigation Lights
+        ['GB030', ['gb030']], // Helipad
+        ['GB040', ['gb040']], // Launch Pad
+        ['SU001', ['su001']], // Military Installation
+        ['ZD040', ['zd040']], // Annotated Location
+        ];
+      // Funky but it makes life easier
+      var llayerName = layerName.toString().toLowerCase();
+
+      for (var row in fCodeMap)
+      {
+        for (var val in fCodeMap[row][1])
+        {
+          if (llayerName == fCodeMap[row][1][val])
+          {
+            attrs.F_CODE = fCodeMap[row][0];
+            break;
+          }
+        }
+      }
+    } // End of Find an FCode
+
+    // The FCODE for Buildings is different. TDS uses AL013
+    if (attrs.F_CODE == 'AL015') attrs.F_CODE = 'AL013';
+
+    // Swap the F_CODE for a Ferry Terminal
+    if (attrs.F_CODE == 'AQ125' && attrs.TRS == '7')
     {
-        // Drop the FCSUBTYPE since we don't use it
-        if (attrs.FCSUBTYPE) delete attrs.FCSUBTYPE;
+      attrs.F_CODE = 'AQ080'; // TDS/GGDM fcode for Ferry Terminal
+    }
 
-        // The swap list. These are the same attr, just named differently
-        // These may get converted back on output
-        var swapList = {
-                'CPYRT_NOTE':'CCN',
-                'SRC_INFO':'SDP',
-                'SRC_DATE':'SDV',
-                'SMC':'MCC'
-                };
+  }, // End of applyToOsmPreProcessing
 
-        // List of data values to drop/ignore
-        var ignoreList = { '-32765':1,'-32767':1,'-32768':1,
-                           '998':1,'-999999':1,
-                           'n_a':1,'noinformation':1,'unknown':1,'unk':1 };
+  // Post Processing: Lots of cleanup
+  applyToOsmPostProcessing : function (attrs, tags, layerName, geometryType)
+  {
+    // Unpack the TXT field
+    if (tags.note)
+    {
+      var tObj = translate.unpackMemo(tags.note);
+
+      if (tObj.tags !== '')
+      {
+        var tTags = JSON.parse(tObj.tags)
+        for (i in tTags)
+        {
+          // Debug
+          // print('Memo: Add: ' + i + ' = ' + tTags[i]);
+          if (tags[tTags[i]]) hoot.logWarn('Unpacking TXT, overwriting ' + i + ' = ' + tags[i] + '  with ' + tTags[i]);
+          tags[i] = tTags[i];
+        }
+      }
+
+      if (tObj.text !== '')
+      {
+        tags.note = tObj.text;
+      }
+      else
+      {
+        delete tags.note;
+      }
+    } // End process tags.note
+
+    // Calculate accuracy: taken straight from facc.py
+    // 1/30 inch * SCALE for standard
+    // NGA standard is +/-25m 90% circular error (CE) for vector data
+    // Error is only for well defined points, not the line connecting points
+    var accuracy = -1
+    // if ('ACE_EVAL' in attrs && attrs['ACE_EVAL'] != '')
+    if (translate.isOK(attrs.ACE_EVAL))
+    {
+      // Convert to 95% circular error (AKA ~2 standard deviations)
+      accuracy = attrs.ACE_EVAL * 1.91;
+    }
+    // else if ('ACE' in attrs && attrs['ACE'] != '')
+    else if (translate.isOK(attrs.ACE))
+    {
+      // Convert to 95% circular error (AKA ~2 standard deviations)
+      accuracy = attrs.ACE_EVAL * 1.91;
+    }
+    // else if ('SCALE' in attrs && attrs['SCALE'] != '')
+    else if (translate.isOK(attrs.SCALE))
+    {
+      var scale = attrs.SCALE;
+      // Convert to accuracy as 90% circular error
+      accuracy = scale * 1.0 / 30.0 / 12 * 0.3048;
+      // Convert to 95% circular error (AKA ~2 standard deviations)
+      accuracy = accuracy * 1.91;
+    }
+
+    // if ('ACC' in attrs)
+    if (translate.isOK(attrs.ACC))
+    {
+      switch (attrs.ACC)
+      {
+        case '2':
+          accuracy = accuracy * 2; // Add note: Accuracy: Approximate
+          break;
+
+        case '3':
+          accuracy = accuracy * 4; // Add note: Accuracy: Doubtful
+          break;
+
+        case '5':
+          accuracy = -1; // Add note: Accuracy: Disputed }
+          break;
+
+        case '6': // Add note: Accuracy: Undisputed
+        case '7': // Add note: Accuracy: Precise
+          break;
+
+        case '8':
+          accuracy = -1; // Add note: Accuracy: Abrogated
+          break;
+      }
+    }
+
+    if (accuracy > -1)
+    {
+      tags['error:circular'] = accuracy;
+    }
 
-        // Unit conversion. Some attributes are in centimetres, others in decimetres
-        // var unitList = { 'GAW':100,'HCA':10,'WD1':10,'WD2':10,'WD3':10,'WT2':10 };
+    // #####
+    if (attrs.HWT && attrs.HWT !== '0')
+    {
+      tags.amenity = 'place_of_worship';
 
-        for (var col in attrs)
+      if (tags.building)
+      {
+        switch (tags.building)
         {
-            // slightly ugly but we would like to account for: 'No Information','noInformation' etc
-            // First, push to lowercase
-            var attrValue = attrs[col].toString().toLowerCase();
+          case 'cathedral':
+          case 'chapel':
+          case 'church':
+            tags.religion = 'christian';
+            break;
+
+          case 'marabout':
+          case 'mosque':
+            tags.religion = 'muslim';
+            break;
+
+          case 'synagogue':
+            tags.religion = 'jewish';
+            break;
+
+          case 'stupa':
+            religion = 'buddhist';
+            break;
+
+          // In the spec, these don't specify a religion
+          // case 'religious_community':
+          // case 'pagoda':
+          // case 'shrine':
+          // case 'tabernacle':
+          // case 'temple':
+        } // End switch
+      }
 
-            // Get rid of the spaces in the text
-            attrValue = attrValue.replace(/\s/g, '');
+      if (tags['tower:type'] == 'minaret')
+      {
+        tags.religion = 'muslim';
+      }
+    } // End HWT
 
-            // Wipe out the useless values
-            // if (attrs[col] == '-32768' || attrValue == 'unk' || attrValue == 'n_a' || attrValue == 'noinformation' || attrs[col] == '')
-            if (attrs[col] == '' || attrValue in ignoreList || attrs[col] in ignoreList)
-            {
-                // debug: Comment this out to leave all of the No Info stuff in for testing
-                delete attrs[col];
-                continue;
-            }
+    // Add the LayerName to the source
+    if ((! tags.source) && layerName !== '') tags.source = 'mgcp:' + layerName.toLowerCase();
 
-            // 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
-            }
+    // If we have a UID, store it
+    if (tags.uuid)
+    {
+      tags.uuid = '{' + tags['uuid'].toString().toLowerCase()  + '}'
+    }
+    else
+    {
+      if (mgcp.configIn.OgrAddUuid == 'true') tags.uuid = createUuid();
+    }
 
-            // Sort out units - if needed
-            // if (col in unitList) attrs[col] = attrs[col] / unitList[col];
+    // Railway Yard
+    // if (attrs.F_CODE == 'AN060' && tags.landuse =='railway') tags.service = 'yard';
 
-            // Now see if we need to swap attr names
-            if (col in swapList)
-            {
-                // print('Swapped: ' + swapList[i]); // debug
-                attrs[swapList[col]] = attrs[col];
-                delete attrs[col];
-                continue
-            }
-        } // End col in attrs loop
+    // Communications Tower
+    // if (attrs.F_CODE == 'AT080') tags['tower:type'] = 'communication';
 
-        if (attrs.F_CODE)
-        {
-            // Drop the the "Not Found" F_CODE. This is from the UI
-            // NOTE: We _should_ be getting "FCODE" not "F_CODE" from files/UI
-            if (attrs.F_CODE == 'Not found') delete attrs.F_CODE;
-        }
-        else if (attrs.FCODE)
-        {
-            // Swap these since the rest of the lookup tables & TDS use F_CODE
-            if (attrs.FCODE !== 'Not found') attrs.F_CODE = attrs.FCODE;
+    // Railway vs Road
+    // if (attrs.F_CODE == 'AN010' && attrs.RRC =='0') tags.railway = 'yes';
 
-            delete attrs.FCODE;
-        }
-        else
-        {
-            // Time to find an FCODE based on the filename
-            var fCodeMap = [
-                ['AA010', ['aa010']], // Extraction Mine
-                ['AA012', ['aa012']], // Quarry
-                ['AA040', ['aa040']], // Rig
-                ['AA050', ['aa050','well_p']], // Well
-                ['AC000', ['ac000']], // Processing Facility TRDv3 only
-                ['AC020', ['ac020']], // Catalytic Cracker
-                ['AD030', ['ad030']], // Power Substation
-                ['AF010', ['af010']], // Smokestack
-                ['AF030', ['af030']], // Cooling Tower: TRDv30 and TDS but not TRDv4
-                ['AF040', ['af040']], // Crane
-                ['AF070', ['af070']], // Flare Pipe
-                ['AH050', ['ah050']], // Fortification
-                ['AH070', ['ah070']], // Check Point
-                ['AJ051', ['aj051']], // Wind Turbine
-                ['AK040', ['ak040']], // Sports Ground
-                ['AK160', ['ak160']], // Stadium
-                ['AL015', ['al015','building']], // Building
-                ['AL020', ['al020']], // Built up area
-                ['AL025', ['al025']], // Cairn
-                ['AL030', ['al030']], // Cemetary
-                ['AL080', ['al080']], // Gantry
-                ['AL100', ['al100']], // Hut
-                ['AL105', ['al105']], // Settlement
-                ['AL130', ['al130']], // Memorial Monument
-                ['AL200', ['al200']], // Ruins
-                ['AL240', ['al240']], // Non-communication Tower - TRD30
-                ['AL241', ['al241']], // Tower - TRD40
-                ['AM020', ['am020']], // Grain Storage Structure
-                ['AM030', ['am030']], // Grain Elevator
-                ['AM040', ['am040']], // Mineral Pile
-                ['AM060', ['am060']], // Surface Bunker
-                ['AM070', ['am070']], // Storage Tank
-                ['AM070', ['am080']], // Water Tower
-                ['AN010', ['an010','railroad_l']], // Railway
-                ['AN050', ['an050']], // Railway Sidetrack
-                ['AN060', ['an060']], // Railway Yard
-                ['AN075', ['an075']], // Railway Turntable
-                ['AP010', ['ap010','trackl','track_l','cart_track','carttrack','cart_track_l']], // Cart Track
-                ['AP020', ['ap020','interchange_l','interchangel']], // Interchange
-                ['AP030', ['ap030','roadnet','road_l','roadl']], // Road
-                ['AP050', ['ap050','traill','trail_l']], // Trail
-                ['AQ040', ['aq040','bridgel','bridge_l','bridge_tunnel_l','bridge_overpass_l']], // Bridge
-                ['AQ060', ['aq060']], // Control Tower
-                ['AQ065', ['aq065']], // Culvert
-                ['AQ090', ['aq090']], // Entrance and/or Exit
-                ['AQ113', ['aq113']], // Pipeline
-                ['AQ116', ['aq116']], // Pumping Station
-                ['AQ125', ['aq125']], // Transportation Station
-                ['AQ130', ['aq130','tunnell','tunnel_l']], // Tunnel
-                ['AT010', ['at010']], // Dish Aerial
-                ['AT042', ['at042']], // Pylon
-                ['AT045', ['at045']], // Radar Station
-                // ['AT050', ['at050']], // Communication Station
-                ['AT080', ['at080']], // Communication Tower
-                ['BH010', ['bh010']], // Aqueduct
-                ['BH020', ['bh020']], // Canal
-                ['BH030', ['bh030']], // Ditch
-                ['BH070', ['bh070']], // Ford
-                ['BH140', ['bh140','river_stream_l']], // River
-                ['BH145', ['bh145']], // Vanishing Point
-                ['BH170', ['bh170']], // Natural Pool
-                ['BI010', ['bi010']], // Cistern
-                ['BI020', ['bi020']], // Dam
-                ['BI040', ['bi040']], // Sluice Gate
-                ['BI050', ['bi050']], // Water Intake Tower
-                ['DB030', ['db030']], // Cave
-                ['DB070', ['db070']], // Cut
-                ['DB150', ['db150']], // Mountain Pass
-                ['GA035', ['ga035']], // Aeronautical NAVAID - TRDv30
-                ['GB010', ['gb010']], // Airport Navigation Lights
-                ['GB030', ['gb030']], // Helipad
-                ['GB040', ['gb040']], // Launch Pad
-                ['SU001', ['su001']], // Military Installation
-                ['ZD040', ['zd040']], // Annotated Location
-                ];
-            // Funky but it makes life easier
-            var llayerName = layerName.toString().toLowerCase();
-
-            for (var row in fCodeMap)
-            {
-                for (var val in fCodeMap[row][1])
-                {
-                    if (llayerName == fCodeMap[row][1][val])
-                    {
-                        attrs.F_CODE = fCodeMap[row][0];
-                        break;
-                    }
-                }
-            }
-        } // End of Find an FCode
+    if (mgcp.osmPostRules == undefined)
+    {
+      // "New" style complex rules
+      //
+      // 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['building:religious'] == 'other'","t.amenity = 'religion'"],
+      ["t['cable:type'] && !(t.cable)","t.cable = 'yes'"],
+      ["t.control_tower == 'yes'","t['tower:type'] = 'observation'; t.use = 'air_traffic_control'"],
+      ["t['generator:source'] == 'wind'","t.power = 'generator'"],
+      ["(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['monitoring:weather'] == 'yes'","t.man_made = 'monitoring_station'"],
+      ["t.natural =='spring' && t['spring:type'] == 'spring'","delete t['spring:type']"],
+      ["t.public_transport == 'station'","t.bus = 'yes'"],
+      ["t.pylon =='yes' && t['cable:type'] == 'power'"," t.power = 'tower'"],
+      ["t['social_facility:for'] == 'senior'","t.amenity = 'social_facility'; t.social_facility = 'group_home'"],
+      ["t['tower:type'] && !(t.man_made)","t.man_made = 'tower'"],
+      ["t.water && !(t.natural)","t.natural = 'water'"],
+      ["t.waterway == 'flow_control'","t.flow_control = 'sluice_gate'"]
+      ];
+
+      mgcp.osmPostRules = translate.buildComplexRules(rulesList);
+    }
+
+    // translate.applyComplexRules(tags,attrs,mgcp.osmPostRules);
+    // Pulling this out of translate
+    for (var i = 0, rLen = mgcp.osmPostRules.length; i < rLen; i++)
+    {
+      if (mgcp.osmPostRules[i][0](tags)) mgcp.osmPostRules[i][1](tags,attrs);
+    }
 
-        // The FCODE for Buildings is different. TDS uses AL013
-        if (attrs.F_CODE == 'AL015') attrs.F_CODE = 'AL013';
+    // 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
+
+    // Add 'building = yes' to amenities if we don't already have one
+    // if (tags.amenity && !tags.building)
+    // {
+    //   // Debug:
+    //   // print('Added building');
+    //   // Don't add building=yes to built up areas!
+    //   if (!tags.place) tags.building = 'yes';
+    // }
+
+    // if (tags.building == 'train_station' && !tags.railway) tags.railway = 'station';
+    // if ('ford' in tags && !tags.highway) tags.highway = 'road';
+
+    // Some FCODE specific rules
+    switch (attrs.F_CODE)
+    {
+      case undefined: // Break early if no value
+        break;
 
-        // Swap the F_CODE for a Ferry Terminal
-        if (attrs.F_CODE == 'AQ125' && attrs.TRS == '7')
-        {
-            attrs.F_CODE = 'AQ080'; // TDS/GGDM fcode for Ferry Terminal
-        }
+      case 'AA052': // Hydrocarbons Field
+        tags.landuse = 'industrial';
+        break;
 
-    }, // End of applyToOsmPreProcessing
+      case 'AF030': // Cooling Tower
+        if (!tags['tower:type']) tags['tower:type'] = 'cooling';
+        break;
 
-    // Post Processing: Lots of cleanup
-    applyToOsmPostProcessing : function (attrs, tags, layerName, geometryType)
-    {
-        // Unpack the TXT field
-        if (tags.note)
-        {
-            var tObj = translate.unpackMemo(tags.note);
+      case 'AL013': // Building  NOTE this is the TDS F_CODE for Building. This was swapped during pre-processing
+        if (tags.surface == 'unknown') delete tags.surface;
+        break;
 
-            if (tObj.tags !== '')
-            {
-                var tTags = JSON.parse(tObj.tags)
-                for (i in tTags)
-                {
-                    // Debug
-                    // print('Memo: Add: ' + i + ' = ' + tTags[i]);
-                    if (tags[tTags[i]]) hoot.logWarn('Unpacking TXT, overwriting ' + i + ' = ' + tags[i] + '  with ' + tTags[i]);
-                    tags[i] = tTags[i];
-                }
-            }
+      case 'AL020': // AL020 (Built-up Area) should become a Place. NOTE: This is a bit vague...
+        tags.place = 'yes'; // Catch All
 
-            if (tObj.text !== '')
-            {
-                tags.note = tObj.text;
-            }
-            else
-            {
-                delete tags.note;
-            }
-        } // End process tags.note
-
-        // Calculate accuracy: taken straight from facc.py
-        // 1/30 inch * SCALE for standard
-        // NGA standard is +/-25m 90% circular error (CE) for vector data
-        // Error is only for well defined points, not the line connecting points
-        var accuracy = -1
-        // if ('ACE_EVAL' in attrs && attrs['ACE_EVAL'] != '')
-        if (translate.isOK(attrs.ACE_EVAL))
-        {
-            // Convert to 95% circular error (AKA ~2 standard deviations)
-            accuracy = attrs.ACE_EVAL * 1.91;
-        }
-        // else if ('ACE' in attrs && attrs['ACE'] != '')
-        else if (translate.isOK(attrs.ACE))
-        {
-            // Convert to 95% circular error (AKA ~2 standard deviations)
-            accuracy = attrs.ACE_EVAL * 1.91;
-        }
-        // else if ('SCALE' in attrs && attrs['SCALE'] != '')
-        else if (translate.isOK(attrs.SCALE))
+        switch (tags['place:importance'])
         {
-            var scale = attrs.SCALE;
-            // Convert to accuracy as 90% circular error
-            accuracy = scale * 1.0 / 30.0 / 12 * 0.3048;
-            // Convert to 95% circular error (AKA ~2 standard deviations)
-            accuracy = accuracy * 1.91;
-        }
+          case undefined: // Break early if no value
+            break;
 
-        // if ('ACC' in attrs)
-        if (translate.isOK(attrs.ACC))
-        {
-            switch (attrs.ACC)
-            {
-                case '2':
-                    accuracy = accuracy * 2; // Add note: Accuracy: Approximate
-                    break;
+          case 'first':
+            tags.place = 'city';
+            tags.capital = 'yes'
+            break;
 
-                case '3':
-                    accuracy = accuracy * 4; // Add note: Accuracy: Doubtful
-                    break;
+          case 'second':
+            tags.place = 'city';
+            break;
 
-                case '5':
-                    accuracy = -1; // Add note: Accuracy: Disputed }
-                    break;
+          case 'third':
+          case 'fourth':
+            tags.place = 'town';
+            break;
 
-                case '6': // Add note: Accuracy: Undisputed
-                case '7': // Add note: Accuracy: Precise
-                    break;
+          case 'fifth':
+            tags.place = 'village';
+            break;
 
-                case '8':
-                    accuracy = -1; // Add note: Accuracy: Abrogated
-                    break;
-            }
-        }
+          case 'sixth':
+            tags.place = 'hamlet';
+            break;
+        } // End switch
 
-        if (accuracy > -1)
+        switch (tags.use) // Fixup the landuse tags
         {
-            tags['error:circular'] = accuracy;
-        }
+          case undefined: // Break early if no value
+            break;
 
-        // #####
-        if (attrs.HWT && attrs.HWT !== '0')
-        {
-            tags.amenity = 'place_of_worship';
+          case 'industrial':
+            tags.landuse = 'industrial';
+            delete tags.use;
+            break;
 
-            if (tags.building)
-            {
-                switch (tags.building)
-                {
-                    case 'cathedral':
-                    case 'chapel':
-                    case 'church':
-                        tags.religion = 'christian';
-                        break;
-
-                    case 'marabout':
-                    case 'mosque':
-                        tags.religion = 'muslim';
-                        break;
-
-                    case 'synagogue':
-                        tags.religion = 'jewish';
-                        break;
-
-                    case 'stupa':
-                        religion = 'buddhist';
-                        break;
-
-                    // In the spec, these don't specify a religion
-                    // case 'religious_community':
-                    // case 'pagoda':
-                    // case 'shrine':
-                    // case 'tabernacle':
-                    // case 'temple':
-                } // End switch
-            }
+          case 'commercial':
+            tags.landuse = 'commercial';
+            delete tags.use;
+            break;
 
-            if (tags['tower:type'] == 'minaret')
-            {
-                tags.religion = 'muslim';
-            }
-        } // End HWT
+          case 'residential':
+            tags.landuse = 'residential';
+            delete tags.use;
+            break;
+        } // End switch
 
-        // Add the LayerName to the source
-        if ((! tags.source) && layerName !== '') tags.source = 'mgcp:' + layerName.toLowerCase();
+        break;
 
-        // If we have a UID, store it
-        if (tags.uuid)
-        {
-            tags.uuid = '{' + tags['uuid'].toString().toLowerCase()  + '}'
-        }
-        else
+      case 'AQ125': // Transportation Station
+        if (tags.amenity == 'ferry_terminal')
         {
-            if (mgcp.configIn.OgrAddUuid == 'true') tags.uuid = createUuid();
+          attrs.TRS = '7';
+          if (tags.bus) delete tags.bus;
         }
+        break;
 
-        // Railway Yard
-        // if (attrs.F_CODE == 'AN060' && tags.landuse =='railway') tags.service = 'yard';
+      case 'BH070': // Ford
+        // Fords are also supposed to be roads
+        if (geometryType == 'Line' && !tags.highway) tags.highway = 'road';
+        break;
 
-        // Communications Tower
-        // if (attrs.F_CODE == 'AT080') tags['tower:type'] = 'communication';
+      case 'GB485': // Approach Lighting System
+        tags.navigationaid = 'als';
+        break;
 
-        // Railway vs Road
-        // if (attrs.F_CODE == 'AN010' && attrs.RRC =='0') tags.railway = 'yes';
+      case 'AH050': // Fortification
+        // Castles are not Bunkers but they get stored in the same layer
+        if (tags.military == 'bunker' && tags.historic == 'castle') delete tags.military;
+        break;
+    } // End switch FCODE
 
-        if (mgcp.osmPostRules == undefined)
-        {
-            // "New" style complex rules
-            //
-            // 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['building:religious'] == 'other'","t.amenity = 'religion'"],
-            ["t['cable:type'] && !(t.cable)","t.cable = 'yes'"],
-            ["t.control_tower == 'yes'","t['tower:type'] = 'observation'; t.use = 'air_traffic_control'"],
-            ["t['generator:source'] == 'wind'","t.power = 'generator'"],
-            ["(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['monitoring:weather'] == 'yes'","t.man_made = 'monitoring_station'"],
-            ["t.natural =='spring' && t['spring:type'] == 'spring'","delete t['spring:type']"],
-            ["t.public_transport == 'station'","t.bus = 'yes'"],
-            ["t.pylon =='yes' && t['cable:type'] == 'power'"," t.power = 'tower'"],
-            ["t['social_facility:for'] == 'senior'","t.amenity = 'social_facility'; t.social_facility = 'group_home'"],
-            ["t['tower:type'] && !(t.man_made)","t.man_made = 'tower'"],
-            ["t.water && !(t.natural)","t.natural = 'water'"],
-            ["t.waterway == 'flow_control'","t.flow_control = 'sluice_gate'"]
-            ];
-
-            mgcp.osmPostRules = translate.buildComplexRules(rulesList);
-        }
+    // Sort out TRS (Transport Type)
+    switch (attrs.TRS)
+    {
+      case undefined:
+        break;
+
+      case '9': // Pedestrian
+        // NOTE: This _might_ be a path: AP050 (Trail)
+        if (!tags.highway) tags.highway = 'track';
+        break;
+
+      case '12': // Railway
+        if (!tags.railway) tags.railway = 'rail';
+        break;
+
+      case '3': // TRD3 'Automotive'
+      case '4': // Bus
+      case '13': // Road
+        if (!tags.highway) tags.highway = 'road';
+        break;
+
+      case '14': // Road and Railway. This might be ugly....
+        if (!tags.highway) tags.highway = 'road';
+        if (!tags.railway) tags.railway = 'rail';
+        break;
+    } // End switch TRS
+
+    // Content vs Product for storage tanks
+    if (tags.product && tags.man_made == 'storage_tank')
+    {
+      tags.content = tags.product;
+      delete tags.product;
+    }
+
+    // AC000 (Processing Facility) vs AL010 (Facility)
+    // In TDS, this is just AL010. Therefore, make it AL010 and use a custom rule if we are exporting
+    // We are assumeing that it should produce something
+    if (tags.facility == 'processing')
+    {
+      if (! tags.product) tags.product = 'unknown';
+      tags.facility = 'yes';
+    }
 
-        // translate.applyComplexRules(tags,attrs,mgcp.osmPostRules);
-        // Pulling this out of translate
-        for (var i = 0, rLen = mgcp.osmPostRules.length; i < rLen; i++)
-        {
-            if (mgcp.osmPostRules[i][0](tags)) mgcp.osmPostRules[i][1](tags,attrs);
-        }
+    // Sort out the WID, WD1 etc attributes
+    // Roads etc have a WD1 attribute but this doesn't get translated to "width"
+    if (attrs.WD1)
+    {
+      if (! tags.width) tags.width = attrs.WD1;
+    }
 
-        // 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
-
-        // Add 'building = yes' to amenities if we don't already have one
-        // if (tags.amenity && !tags.building)
-        // {
-        //     // Debug:
-        //     // print('Added building');
-        //     // Don't add building=yes to built up areas!
-        //     if (!tags.place) tags.building = 'yes';
-        // }
-
-        // if (tags.building == 'train_station' && !tags.railway) tags.railway = 'station';
-        // if ('ford' in tags && !tags.highway) tags.highway = 'road';
-
-        // Some FCODE specific rules
-        switch (attrs.F_CODE)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'AA052': // Hydrocarbons Field
-                tags.landuse = 'industrial';
-                break;
-
-            case 'AF030': // Cooling Tower
-                if (!tags['tower:type']) tags['tower:type'] = 'cooling';
-                break;
-
-            case 'AL013': // Building  NOTE this is the TDS F_CODE for Building. This was swapped during pre-processing
-                if (tags.surface == 'unknown') delete tags.surface;
-                break;
-
-            case 'AL020': // AL020 (Built-up Area) should become a Place. NOTE: This is a bit vague...
-                tags.place = 'yes'; // Catch All
-
-                switch (tags['place:importance'])
-                {
-                    case undefined: // Break early if no value
-                        break;
-
-                    case 'first':
-                        tags.place = 'city';
-                        tags.capital = 'yes'
-                        break;
-
-                    case 'second':
-                        tags.place = 'city';
-                        break;
-
-                    case 'third':
-                    case 'fourth':
-                        tags.place = 'town';
-                        break;
-
-                    case 'fifth':
-                        tags.place = 'village';
-                        break;
-
-                    case 'sixth':
-                        tags.place = 'hamlet';
-                        break;
-                } // End switch
-
-                switch (tags.use) // Fixup the landuse tags
-                {
-                    case undefined: // Break early if no value
-                        break;
-
-                    case 'industrial':
-                        tags.landuse = 'industrial';
-                        delete tags.use;
-                        break;
-
-                    case 'commercial':
-                        tags.landuse = 'commercial';
-                        delete tags.use;
-                        break;
-
-                    case 'residential':
-                        tags.landuse = 'residential';
-                        delete tags.use;
-                        break;
-                } // End switch
-
-                break;
-
-            case 'AQ125': // Transportation Station
-                if (tags.amenity == 'ferry_terminal')
-                {
-                    attrs.TRS = '7';
-                    if (tags.bus) delete tags.bus;
-                }
-                break;
-
-            case 'BH070': // Ford
-                // Fords are also supposed to be roads
-                if (geometryType == 'Line' && !tags.highway) tags.highway = 'road';
-                break;
-
-            case 'GB485': // Approach Lighting System
-                tags.navigationaid = 'als';
-                break;
-
-            case 'AH050': // Fortification
-                // Castles are not Bunkers but they get stored in the same layer
-                if (tags.military == 'bunker' && tags.historic == 'castle') delete tags.military;
-                break;
-        } // End switch FCODE
-
-        // Sort out TRS (Transport Type)
-        switch (attrs.TRS)
-        {
-            case undefined:
-                break;
-
-            case '9': // Pedestrian
-                // NOTE: This _might_ be a path: AP050 (Trail)
-                if (!tags.highway) tags.highway = 'track';
-                break;
-
-            case '12': // Railway
-                if (!tags.railway) tags.railway = 'rail';
-                break;
-
-            case '3': // TRD3 'Automotive'
-            case '4': // Bus
-            case '13': // Road
-                if (!tags.highway) tags.highway = 'road';
-                break;
-
-            case '14': // Road and Railway. This might be ugly....
-                if (!tags.highway) tags.highway = 'road';
-                if (!tags.railway) tags.railway = 'rail';
-                break;
-        } // End switch TRS
-
-        // Content vs Product for storage tanks
-        if (tags.product && tags.man_made == 'storage_tank')
-        {
-            tags.content = tags.product;
-            delete tags.product;
-        }
+    // Fix up areas
+    // The thought is: If Hoot thinks it's an area but OSM doesn't think it's an area, make it an area
+    if (geometryType == 'Area' && ! translate.isOsmArea(tags))
+    {
+      // Debug
+      // print('Adding area=yes');
+      tags.area = 'yes';
+    }
+
+  }, // End of applyToOsmPostProcessing
+
+  // ##### Start of the xxToMgcpxx Block #####
+  applyToMgcpPreProcessing: 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" 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 and general cleaning
+    // 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;
 
-        // AC000 (Processing Facility) vs AL010 (Facility)
-        // In TDS, this is just AL010. Therefore, make it AL010 and use a custom rule if we are exporting
-        // We are assumeing that it should produce something
-        if (tags.facility == 'processing')
-        {
-            if (! tags.product) tags.product = 'unknown';
-            tags.facility = 'yes';
-        }
+      case 'abandoned':
+      case 'disused':
+        tags.highway = 'road';
+        tags.condition = 'abandoned';
+        break;
+
+      case 'demolished':
+        tags.highway = 'road';
+        tags.condition = 'dismantled';
+        break;
 
-        // Sort out the WID, WD1 etc attributes
-        // Roads etc have a WD1 attribute but this doesn't get translated to "width"
-        if (attrs.WD1)
+      case 'construction':
+        if (tags.construction)
         {
-            if (! tags.width) tags.width = attrs.WD1;
+          tags.highway = tags.construction;
+          delete tags.construction;
         }
-
-        // Fix up areas
-        // The thought is: If Hoot thinks it's an area but OSM doesn't think it's an area, make it an area
-        if (geometryType == 'Area' && ! translate.isOsmArea(tags))
+        else
         {
-            // Debug
-            // print('Adding area=yes');
-            tags.area = 'yes';
+          tags.highway = 'road';
         }
+        tags.condition = 'construction';
+        break;
+
+      // Doing this here so we preserve the "highway" tag
+      case 'bridleway':
+      case 'cycleway':
+      case 'footway':
+      case 'steps':
+        attrs.F_CODE = 'AP050'; // Trail
+        break;
 
-    }, // End of applyToOsmPostProcessing
+      case 'service':
+      case 'living_street':
+      case 'pedestrian':
+        attrs.F_CODE = 'AP030'; // Road
+        break;
+    } // End highway
 
-    // ##### Start of the xxToMgcpxx Block #####
-    applyToMgcpPreProcessing: function(tags, attrs, geometryType)
+    switch (tags.railway)
     {
-        // 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 undefined: // Break early if no value
+        break;
 
-        // initial cleanup
-        for (var i in tags)
-        {
-            // Remove empty tags
-            if (tags[i] == '')
-            {
-                delete tags[i];
-                continue;
-            }
+      case 'abandoned':
+      case 'disused':
+        tags.railway = 'rail';
+        tags.condition = 'abandoned';
+        break;
 
-            // Convert "abandoned: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;
-            }
+      case 'demolished':
+        tags.railway = 'yes';
+        tags.condition = 'dismantled';
+        break;
 
-            // 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 and general cleaning
-        // 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;
-
-            // Doing this here so we preserve the "highway" tag
-            case 'bridleway':
-            case 'cycleway':
-            case 'footway':
-            case 'steps':
-                attrs.F_CODE = 'AP050'; // Trail
-                break;
-
-            case 'service':
-            case 'living_street':
-            case 'pedestrian':
-                attrs.F_CODE = 'AP030'; // Road
-                break;
-        } // End highway
-
-        switch (tags.railway)
+      case 'construction':
+        if (tags.construction)
         {
-            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;
+          tags.railway = tags.construction;
+          delete tags.construction;
         }
-
-        switch (tags.building)
+        else
         {
-            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;
+          tags.railway = 'rail';
         }
+        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;
-        }
+    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;
 
-        if (mgcp.mgcpPreRules == undefined)
+      case 'demolished':
+        tags.building = 'yes';
+        tags.condition = 'dismantled';
+        break;
+
+      case 'construction':
+        if (tags.construction)
         {
-            // See ToOsmPostProcessing for more details about rulesList
-            var rulesList = [
-            // ["t.amenity == 'marketplace'","t.facility = 'yes'"],
-            ["t.amenity == 'ferry_terminal'","t['transport:type'] = 'maritime'"],
-            ["t.aeroway == 'navigationaid' && t.navigationaid","delete t.navigationaid"],
-            ["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.construction && t.railway","t.railway = t.construction; t.condition = 'construction'; delete t.construction"],
-            // ["t.construction && t.highway","t.highway = t.construction; t.condition = 'construction'; delete t.construction"],
-            ["t.content && !(t.product)","t.product = t.content; delete t.content"],
-            ["t.landuse == 'railway' && t['railway:yard'] == 'marshalling_yard'","a.F_CODE = 'AN060'"],
-            ["t.leisure == 'stadium' && t.building","delete t.building"],
-            ["t.man_made && t.building == 'yes'","delete t.building"],
-            ["t.man_made == 'water_tower'","a.F_CODE = 'AL241'"],
-            ["t.natural == 'sinkhole'","a.F_CODE = 'BH145'; t['water:sink:type'] = 'disappearing'; delete t.natural"],
-            ["t.natural == 'spring' && !(t['spring:type'])","t['spring:type'] = 'spring'"],
-            ["t.natural == 'wood'","t.landuse = 'forest'"],
-            ["t.power == 'generator'","a.F_CODE = 'AL015'; t.use = 'power_generation'"],
-            //["t.power == 'line'","t['cable:type'] = 'power'; t.cable = 'yes'"],
-            ["t.power == 'tower'","t['cable:type'] = 'power'; t.pylon = 'yes'; delete t.power"],
-            ["t.rapids == 'yes'","t.waterway = 'rapids'"],
-            ["t.resource","t.product = 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 = 'AL015'"],
-            ["t.tourism == 'information' && t.information","delete t.tourism"],
-            ["t.tunnel == 'building_passage'","t.tunnel = 'yes'"],
-            ["!(t.water) && t.natural == 'water'","t.water = 'lake'"],
-            ["t.water == 'pond'","a.F_CODE = 'BH170'; t.natural = 'other_pool_type'"],
-            ["t.water == 'river'","t.waterway = 'river'"],
-            ["t.waterway == 'riverbank'","t.waterway = 'river'"],
-            ["t.wetland && t.natural == 'wetland'","delete t.natural"]
-            ];
-
-            mgcp.mgcpPreRules = translate.buildComplexRules(rulesList);
+          tags.building = tags.construction;
+          delete tags.construction;
         }
-
-        // Apply the rulesList
-        //translate.applyComplexRules(tags,attrs,mgcp.mgcpPreRules);
-        for (var i = 0, rLen = mgcp.mgcpPreRules.length; i < rLen; i++)
+        else
         {
-            if (mgcp.mgcpPreRules[i][0](tags)) mgcp.mgcpPreRules[i][1](tags,attrs);
+          tags.building = 'yes';
         }
+        tags.condition = 'construction';
+        break;
+    }
+
+    switch (tags.bridge)
+    {
+      case undefined: // Break early if no value
+        break;
 
-        // Sort out landuse
-        switch (tags.landuse)
+      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)
         {
-            case undefined: // Break early if no value
-                break;
-
-            case 'brownfield':
-                tags.landuse = 'built_up_area';
-                tags.condition = 'destroyed';
-                break
-
-            case 'construction':
-                tags.condition = 'construction';
-                tags.landuse = 'built_up_area';
-                break;
-
-            case 'commercial':
-            case 'retail':
-                tags.use = 'commercial';
-                tags.landuse = 'built_up_area';
-                break;
-
-            case 'farm':
-            case 'allotments':
-                tags.landuse = 'farmland';
-                break;
-
-            case 'farmyard': // NOTE: This is different to farm
-                tags.facility = 'yes';
-                tags.use = 'agriculture';
-                delete tags.landuse;
-                break;
-
-            case 'grass':
-            case 'meadow':
-                tags.natural = 'grassland';
-                tags['grassland:type'] = 'grassland';
-                delete tags.landuse;
-                break;
-
-            case 'industrial': // Deconflict with AA052 Hydrocarbons Field
-                switch (tags.industrial)
-                {
-                    case undefined: // Built up Area
-                        tags.use = 'industrial';
-                        tags.landuse = 'built_up_area';
-                        break;
-
-                    case 'oil':
-                        tags.product = 'petroleum';
-                        tags.industrial = 'hydrocarbons_field';
-                        delete tags.landuse;
-                        break;
-
-                    case 'gas':
-                        tags.product = 'gas';
-                        tags.industrial = 'hydrocarbons_field';
-                        delete tags.landuse;
-                        break;
-
-                    case 'hydrocarbons_field':
-                        delete tags.landuse;
-                        break;
-                }
-                break;
-
-            case 'military':
-                tags.military = 'installation';
-                delete tags.landuse;
-                break;
-
-            case 'residential':
-                tags.use = 'residential';
-                tags.landuse = 'built_up_area';
-                break;
-        } // End switch landuse
-
-
-        // Fix up OSM 'walls' around facilities
-        if (tags.barrier == 'wall' && geometryType == 'Area')
+          tags.bridge = tags.construction;
+          delete tags.construction;
+        }
+        else
         {
-            attrs.F_CODE = 'AL010'; // Facility
-            delete tags.barrier; // Take away the walls...
+          tags.bridge = 'yes';
         }
+        tags.condition = 'construction';
+        break;
+    }
 
-        // Going out on a limb and processing OSM specific tags:
-        // - Building == a thing,
-        // - Amenity == The area around a thing. Except for the ones that are not.....
-        // If it is a Point feature, it becomes a building. Polygon features become Facilities
-        // Line features become buildings and then get ignored
-        var facilityList = {'school':'850','hospital':'860','university':'850','college':'850'};
-        if (tags.amenity in facilityList)
-        {
-            if (geometryType == 'Area')
-            {
-                attrs.F_CODE = 'AL010'; // Facility
+    if (mgcp.mgcpPreRules == undefined)
+    {
+      // See ToOsmPostProcessing for more details about rulesList
+      var rulesList = [
+      // ["t.amenity == 'marketplace'","t.facility = 'yes'"],
+      ["t.amenity == 'ferry_terminal'","t['transport:type'] = 'maritime'"],
+      ["t.aeroway == 'navigationaid' && t.navigationaid","delete t.navigationaid"],
+      ["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.construction && t.railway","t.railway = t.construction; t.condition = 'construction'; delete t.construction"],
+      // ["t.construction && t.highway","t.highway = t.construction; t.condition = 'construction'; delete t.construction"],
+      ["t.content && !(t.product)","t.product = t.content; delete t.content"],
+      ["t.landuse == 'railway' && t['railway:yard'] == 'marshalling_yard'","a.F_CODE = 'AN060'"],
+      ["t.leisure == 'stadium' && t.building","delete t.building"],
+      ["t.man_made && t.building == 'yes'","delete t.building"],
+      ["t.man_made == 'water_tower'","a.F_CODE = 'AL241'"],
+      ["t.natural == 'sinkhole'","a.F_CODE = 'BH145'; t['water:sink:type'] = 'disappearing'; delete t.natural"],
+      ["t.natural == 'spring' && !(t['spring:type'])","t['spring:type'] = 'spring'"],
+      ["t.natural == 'wood'","t.landuse = 'forest'"],
+      ["t.power == 'generator'","a.F_CODE = 'AL015'; t.use = 'power_generation'"],
+      //["t.power == 'line'","t['cable:type'] = 'power'; t.cable = 'yes'"],
+      ["t.power == 'tower'","t['cable:type'] = 'power'; t.pylon = 'yes'; delete t.power"],
+      ["t.rapids == 'yes'","t.waterway = 'rapids'"],
+      ["t.resource","t.product = 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 = 'AL015'"],
+      ["t.tourism == 'information' && t.information","delete t.tourism"],
+      ["t.tunnel == 'building_passage'","t.tunnel = 'yes'"],
+      ["!(t.water) && t.natural == 'water'","t.water = 'lake'"],
+      ["t.water == 'pond'","a.F_CODE = 'BH170'; t.natural = 'other_pool_type'"],
+      ["t.water == 'river'","t.waterway = 'river'"],
+      ["t.waterway == 'riverbank'","t.waterway = 'river'"],
+      ["t.wetland && t.natural == 'wetland'","delete t.natural"]
+      ];
+
+      mgcp.mgcpPreRules = translate.buildComplexRules(rulesList);
+    }
+
+    // Apply the rulesList
+    //translate.applyComplexRules(tags,attrs,mgcp.mgcpPreRules);
+    for (var i = 0, rLen = mgcp.mgcpPreRules.length; i < rLen; i++)
+    {
+      if (mgcp.mgcpPreRules[i][0](tags)) mgcp.mgcpPreRules[i][1](tags,attrs);
+    }
 
-                // If the user has also set a building tag, delete it
-                if (tags.building) delete tags.building;
-            }
-            else
-            {
-                attrs.F_CODE = 'AL015'; // Building
-            }
+    // Sort out landuse
+    switch (tags.landuse)
+    {
+      case undefined: // Break early if no value
+        break;
+
+      case 'brownfield':
+        tags.landuse = 'built_up_area';
+        tags.condition = 'destroyed';
+        break
+
+      case 'construction':
+        tags.condition = 'construction';
+        tags.landuse = 'built_up_area';
+        break;
+
+      case 'commercial':
+      case 'retail':
+        tags.use = 'commercial';
+        tags.landuse = 'built_up_area';
+        break;
+
+      case 'farm':
+      case 'allotments':
+        tags.landuse = 'farmland';
+        break;
+
+      case 'farmyard': // NOTE: This is different to farm
+        tags.facility = 'yes';
+        tags.use = 'agriculture';
+        delete tags.landuse;
+        break;
+
+      case 'grass':
+      case 'meadow':
+        tags.natural = 'grassland';
+        tags['grassland:type'] = 'grassland';
+        delete tags.landuse;
+        break;
+
+      case 'industrial': // Deconflict with AA052 Hydrocarbons Field
+        switch (tags.industrial)
+        {
+          case undefined: // Built up Area
+            tags.use = 'industrial';
+            tags.landuse = 'built_up_area';
+            break;
+
+          case 'oil':
+            tags.product = 'petroleum';
+            tags.industrial = 'hydrocarbons_field';
+            delete tags.landuse;
+            break;
+
+          case 'gas':
+            tags.product = 'gas';
+            tags.industrial = 'hydrocarbons_field';
+            delete tags.landuse;
+            break;
+
+          case 'hydrocarbons_field':
+            delete tags.landuse;
+            break;
+        }
+        break;
+
+      case 'military':
+        tags.military = 'installation';
+        delete tags.landuse;
+        break;
+
+      case 'residential':
+        tags.use = 'residential';
+        tags.landuse = 'built_up_area';
+        break;
+    } // End switch landuse
+
+
+    // 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. Except for the ones that are not.....
+    // If it is a Point feature, it becomes a building. Polygon features become Facilities
+    // Line features become buildings and then get ignored
+    var facilityList = {'school':'850','hospital':'860','university':'850','college':'850'};
+    if (tags.amenity in facilityList)
+    {
+      if (geometryType == 'Area')
+      {
+        attrs.F_CODE = 'AL010'; // Facility
+
+        // If the user has also set a building tag, delete it
+        if (tags.building) delete tags.building;
+      }
+      else
+      {
+        attrs.F_CODE = 'AL015'; // Building
+      }
+
+      // If we don't have a Feature Function then assign one
+      if (!attrs.FFN) attrs.FFN = facilityList[tags.amenity];
+    }
+
+    // AL010 (Facility) vs AC000 (Processing Facility)
+    if (tags.facility && tags.product)
+    {
+      tags.facility = 'processing';
 
-            // If we don't have a Feature Function then assign one
-            if (!attrs.FFN) attrs.FFN = facilityList[tags.amenity];
-        }
+      if (tags.product == 'unknown') delete tags.product;
+    }
 
-        // AL010 (Facility) vs AC000 (Processing Facility)
-        if (tags.facility && tags.product)
-        {
-            tags.facility = 'processing';
+    // More facilities
+    switch (tags.amenity)
+    {
+      case undefined: // Break early
+        break;
+
+      case 'munitions_storage':
+        attrs.F_CODE = 'AM010'; // Storage Depot
+        attrs.PPO = '3'; // Ammunition - close to munitions....
+        delete tags.amenity;
+        break;
+
+      case 'fuel_storage':
+        attrs.F_CODE = 'AM010'; // Storage Depot
+        attrs.PPO = '46'; // Petrol - close but not great
+                  // Other options are: Petroleum:83, Oil:75
+        delete tags.amenity;
+        break;
+    } // End switch Amenity
+
+    // Cutlines and Highways
+    // In OSM, a cutline is a cleared way, if it is a polygon then drop the highway info
+    if (tags.man_made == 'cutline' && geometryType == 'Area' && tags.highway) delete tags.highway;
+
+  /*
+    // Geonames cause problems
+    if (tags.waterway && !tags.intermittent)
+    {
+      if (geometryType == "Point")
+      {
+        // Becomes a named area fCode = 'ZD040';
+        attrs.F_CODE = 'ZD040';
+      }
+      else
+      {
+        // It's now a river of some sort fCode = 'BH140';
+        attrs.F_CODE = 'BH140';
+      }
+    }
+  */
+
+    // Places, localities and regions
+    switch (tags.place)
+    {
+      case undefined: // Break early if no value
+        break;
+
+      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 = 'ZD040'; // Named Location
+        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: If we have a Point, this will goto the O2S layer
+            attrs.F_CODE = 'BA030'; // Island - Polygon
+            delete tags.natural;
+          }
+        }
+        break;
+
+    } // End switch
+
+    // Capitals are important
+    if (tags.capital)
+    {
+      if (!(tags['place:importance'])) tags['place:importance'] = 'first';
+      delete tags.capital;
+    }
+
+    // Built-up-areas & Settlements vs Buildings
+    // If we have a BUA or a Settlement, change the settlement:type tag to a building so we can
+    // go through one2one and get an FFN out
+    if (tags['settlement:type'] && !tags.building)
+    {
+      tags.building = tags['settlement:type'];
+      delete tags['settlement:type'];
+    }
 
-            if (tags.product == 'unknown') delete tags.product;
-        }
+    // Movable Bridges
+    if (tags.bridge == 'movable')
+    {
+      if (! tags['bridge:movable'])
+      {
+      tags['bridge:movable'] = 'unknown';
+      }
+      tags.bridge = 'yes';
+      attrs.F_CODE = 'AQ040';
+    }
+
+    // Viaducts
+    if (tags.bridge == 'viaduct')
+    {
+      tags.bridge = 'yes';
+      tags.note = translate.appendValue(tags.note,'Viaduct',';');
+    }
+
+    // "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
+    }
+
+    // Keep looking for an FCODE
+    // This uses the fcodeLookup tables that are defined earlier
+    // var fcodeLookup = translate.createLookup(fcodeList);
+    if (!attrs.F_CODE)
+    {
+      for (var col in tags)
+      {
+        var value = tags[col];
+        if (col in mgcp.fcodeLookup)
+        {
+          if (value in mgcp.fcodeLookup[col])
+          {
+            var row = mgcp.fcodeLookup[col][value];
+            attrs.F_CODE = row[1];
+          }
+        }
+      }
+    }
+
+    // An "amenity" can be a building or a thing
+    // If appropriate, make the "amenity" into a building
+    // This list is taken from the OSM Wiki and Taginfo
+    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',
+      ]; // End bldArray
+
+    // if (tags.amenity && notBuildingList.indexOf(tags.amenity) == -1 && !tags.building) attrs.F_CODE = 'AL015';
+    if (!(attrs.F_CODE) && !(tags.facility) && tags.amenity && !(tags.building) && (notBuildingList.indexOf(tags.amenity) == -1)) attrs.F_CODE = 'AL015';
+
+    // The FCODE for Buildings changed...
+    if (attrs.F_CODE == 'AL013') attrs.F_CODE = 'AL015';
+
+    // Tag changed
+    if (tags.vertical_obstruction_identifier)
+    {
+      tags['aeroway:obstruction'] = tags.vertical_obstruction_identifier;
+      delete tags.vertical_obstruction_identifier;
+    }
 
-        // More facilities
-        switch (tags.amenity)
-        {
-            case undefined: // Break early
-                break;
-
-            case 'munitions_storage':
-                attrs.F_CODE = 'AM010'; // Storage Depot
-                attrs.PPO = '3'; // Ammunition - close to munitions....
-                delete tags.amenity;
-                break;
-
-            case 'fuel_storage':
-                attrs.F_CODE = 'AM010'; // Storage Depot
-                attrs.PPO = '46'; // Petrol - close but not great
-                                  // Other options are: Petroleum:83, Oil:75
-                delete tags.amenity;
-                break;
-        } // End switch Amenity
-
-        // Cutlines and Highways
-        // In OSM, a cutline is a cleared way, if it is a polygon then drop the highway info
-        if (tags.man_made == 'cutline' && geometryType == 'Area' && tags.highway) delete tags.highway;
-
-    /*
-        // Geonames cause problems
-        if (tags.waterway && !tags.intermittent)
+    // Soil Surface Regions
+    if (! attrs.F_CODE)
+    {
+      if (tags.surface)
+      {
+        attrs.F_CODE = 'DA010'; // Soil Surface Region
+        if (! tags.material)
+        {
+          tags.material = tags.surface;
+          delete tags.surface;
+        }
+      }
+
+      switch (tags.natural)
+      {
+        case 'mud':
+          attrs.F_CODE = 'DA010'; // Soil Surface Region
+          tags.material = tags.surface;
+          break;
+
+        case 'sand':
+          attrs.F_CODE = 'DA010'; // Soil Surface Region
+          tags.material = tags.surface;
+         break;
+
+        case 'bare_rock':
+          attrs.F_CODE = 'DA010'; // Soil Surface Region
+          tags.material = tags.surface;
+          break;
+
+        case 'rock':
+          attrs.F_CODE = 'DA010'; // Soil Surface Region
+          tags.material = tags.surface;
+          break;
+
+        case 'stone':
+          attrs.F_CODE = 'DA010'; // Soil Surface Region
+          tags.material = tags.surface;
+          break;
+
+        case 'scree':
+          attrs.F_CODE = 'DA010'; // Soil Surface Region
+          tags.material = tags.surface;
+          break;
+
+        case 'shingle':
+          attrs.F_CODE = 'DA010'; // Soil Surface Region
+          tags.material = tags.surface;
+          break;
+      }
+    } // End ! F_CODE
+
+    // 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
+
+    // We don't have BH220 in MGCP
+    switch (tags.man_made)
+    {
+      case undefined: // Break early if no value
+        break;
+
+      case 'water_works':
+        delete tags.man_made;
+        attrs.F_CODE = 'AL010'; // Facility
+        attrs.FFN = '350'; // Utilities
+        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;
+    }
+
+    // Names. Sometimes we don't have a name but we do have language ones
+    if (!tags.name) translate.swapName(tags);
+
+  }, // End applyToMgcpPreProcessing
+
+
+  applyToMgcpPostProcessing : function (tags, attrs, geometryType, notUsedTags)
+  {
+    // Gross generalisation. If we don't have an FCODE but we do have an FFN then we either have a
+    // Building or a Facility
+    // In the absence of any other info, we are making it a Building
+    if (!attrs.F_CODE)
+    {
+      if (attrs.FFN) attrs.F_CODE = 'AL015';
+    }
+
+    // If we still don't have an FCODE, try a bit of brute force
+    if (!attrs.F_CODE)
+    {
+      var fcodeMap = {
+        'highway':'AP030','railway':'AN010','building':'AL015',
+        'ford':'BH070','waterway':'BH140','bridge':'AQ040','tomb':'AL036',
+        'railway:in_road':'AN010','tourism':'AL013','mine:access':'AA010',
+        'cutting':'DB070','shop':'AL015','office':'AL015'
+      };
+
+      for (var i in fcodeMap)
+      {
+        if (i in tags)
+        {
+          // Debug
+          // print('Added FCODE from Map: ' + fcodeMap[i]);
+          attrs.F_CODE = fcodeMap[i];
+          break;
+        }
+      }
+    } // End !fcode
+
+    // Sort out the UID
+    if (tags.uuid)
+    {
+      var str = tags['uuid'].split(';');
+      attrs.UID = str[0].replace('{','').replace('}','');
+    }
+    else if (tags['hoot:id'])
+    {
+      attrs.UID = 'raw_id:' + tags['hoot:id'];
+    }
+    else
+    {
+      if (mgcp.configOut.OgrAddUuid == 'true') attrs.UID = createUuid().replace('{','').replace('}','');
+    }
+
+    // Default railway
+    // if (attrs.F_CODE == 'AN010' && tags.railway == 'yes') attrs.RRC = '0';
+
+    // Not a great fit
+    if (tags.public_transportation == 'station' && tags.bus == 'yes') attrs.FFN = '481';
+
+    // Mapping Senior Citizens home to Accomodation. Not great
+    if (tags.amenity == 'social_facility' && tags['social_facility:for'] == 'senior') attrs.FFN = 550;
+
+    // These FCODES have "No prescribed attributes" in TRDv40
+    // Therefore:
+    // - clean up the the attrs
+    // - JSON the tags and stick them in a text field
+    var noAttrList = [ 'AJ010','AJ030','AK170','AL019','AL070','AL099','AN076',
+               'BD100','BD180','BH165','BI010','BJ020','BJ031','BJ110',
+               'DB061','DB071','DB100',
+               'EA020','EA055','EC010','EC040','EC060',
+               'FA090',
+               'GB050',
+               'ZD020',
+             ];
+
+    if (noAttrList.indexOf(attrs.F_CODE) !== -1)
+    {
+      // The metadata tags to skip
+      var skipTags = ['F_CODE','ACC','TXT','UID','SDV','SDP','CCN','SRT'];
+      var tmpAttrs = {};
+
+      for (var i in attrs)
+      {
+        // Skip the metadata tags, save the others, then delete them
+        // from the main list of attrs
+        if (skipTags.indexOf(i) == -1)
         {
-            if (geometryType == "Point")
-            {
-                // Becomes a named area fCode = 'ZD040';
-                attrs.F_CODE = 'ZD040';
-            }
-            else
-            {
-                // It's now a river of some sort fCode = 'BH140';
-                attrs.F_CODE = 'BH140';
-            }
+          tmpAttrs[i] = attrs[i];
+          delete attrs[i];
         }
-    */
+      }
+      attrs.TEXT = JSON.stringify(tmpAttrs);
+    }
 
-        // Places, localities and regions
-        switch (tags.place)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            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 = 'ZD040'; // Named Location
-                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: If we have a Point, this will goto the O2S layer
-                        attrs.F_CODE = 'BA030'; // Island - Polygon
-                        delete tags.natural;
-                    }
-                }
-                break;
+    // Add Weather Restrictions to transportation features
+    if (['AP010','AP030','AP050'].indexOf(attrs.FCODE > -1) && !attrs.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.WTC = '1'; // All weather
+          break;
+
+        case 'path':
+        case 'track':
+          attrs.WTC = '2'; // Fair weather
+          break;
+      }
+
+      // Use the road surface to possibly override the classification
+      // We are assumeing that unpaved roads are Fair Weather only
+      if (attrs.RST == '1') attrs.WTC = '1'; // Hard Paved surface
+      if (attrs.RST == '2') attrs.WTC = '2'; // Unpaved surface
+    }
+
+    // Additional rules for particular FCODE's
+    switch (attrs.F_CODE)
+    {
+      case undefined: // Break early if no value
+        break;
 
-        } // End switch
+      case 'AJ085': // Barn: Valid NFDD/NAS FCODE but not in the MGCP spec
+        attrs.F_CODE = 'AL015'; // Barns are Buildings
+        break;
 
-        // Capitals are important
-        if (tags.capital)
+      case 'AK040': // Athletic Field, Sports Ground
+        if (attrs.MCC)
         {
-            if (!(tags['place:importance'])) tags['place:importance'] = 'first';
-            delete tags.capital;
+          attrs.SMC = attrs.MCC;
+          delete attrs.MCC;
         }
+        break;
 
-        // Built-up-areas & Settlements vs Buildings
-        // If we have a BUA or a Settlement, change the settlement:type tag to a building so we can
-        // go through one2one and get an FFN out
-        if (tags['settlement:type'] && !tags.building)
-        {
-            tags.building = tags['settlement:type'];
-            delete tags['settlement:type'];
-        }
+      case 'AL015': // General Building
+        // Unknown House of Worship
+        if (tags.amenity == 'place_of_worship' && tags.building == 'other') attrs.HWT = 999;
 
-        // Movable Bridges
-        if (tags.bridge == 'movable')
+        if (attrs.HWT && ! tags.amenity && ! attrs.FFN)
         {
-            if (! tags['bridge:movable'])
-            {
-            tags['bridge:movable'] = 'unknown';
-            }
-            tags.bridge = 'yes';
-            attrs.F_CODE = 'AQ040';
+          attrs.FFN = '931';
         }
 
-        // Viaducts
-        if (tags.bridge == 'viaduct')
+        if (attrs.FFN && (attrs.FFN !== '930' && attrs.FFN !== '931'))
         {
-            tags.bridge = 'yes';
-            tags.note = translate.appendValue(tags.note,'Viaduct',';');
+          // Debug
+          //print('AL015: Setting HWT 998');
+          attrs.HWT = '998';
         }
 
-        // "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')
+        // This is a catch all and assigns "Commerce" as the function
+        // Exceptions to this are in the rules list
+        if (!attrs.FFN)
         {
-            delete tags.highway;
-            delete tags.service;
-            attrs.F_CODE = 'AQ140'; // Vehicle lot / car park
+          if (tags.shop || tags.office) attrs.FFN = '440';
         }
+        break;
 
-        // Keep looking for an FCODE
-        // This uses the fcodeLookup tables that are defined earlier
-        // var fcodeLookup = translate.createLookup(fcodeList);
-        if (!attrs.F_CODE)
+      case 'AL020': // Built-up Area
+        // Allowed values for FUC
+        if (['0','1','2','4','19','999'].indexOf(attrs['FUC']) < 0) attrs.FUC = '999';
+        break;
+
+      case 'AL105': // Settlement
+        // Allowed values for FUC
+        if (['0','4','8','19','999'].indexOf(attrs['FUC']) < 0) attrs.FUC = '999';
+        break;
+
+      case 'AN010': // Railway
+        if (tags.bridge) attrs.LOC = '45'; // Above Surface
+        if (tags.tunnel) attrs.LOC = '40'; // Below Surface
+        if (tags.embankment || tags.man_made == 'causeway') attrs.LOC = '44'; // On Surface
+
+        // Single lane roads dont have a median and are not separated
+        // NOTE: This could cause problems
+        if (attrs.LTN == '1')
         {
-            for (var col in tags)
-            {
-                var value = tags[col];
-                if (col in mgcp.fcodeLookup)
-                {
-                    if (value in mgcp.fcodeLookup[col])
-                    {
-                        var row = mgcp.fcodeLookup[col][value];
-                        attrs.F_CODE = row[1];
-                    }
-                }
-            }
+          attrs.SEP = '1000';
+          attrs.MES = '1000';
         }
+        break;
 
-        // An "amenity" can be a building or a thing
-        // If appropriate, make the "amenity" into a building
-        // This list is taken from the OSM Wiki and Taginfo
-        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',
-            ]; // End bldArray
-
-        // if (tags.amenity && notBuildingList.indexOf(tags.amenity) == -1 && !tags.building) attrs.F_CODE = 'AL015';
-        if (!(attrs.F_CODE) && !(tags.facility) && tags.amenity && !(tags.building) && (notBuildingList.indexOf(tags.amenity) == -1)) attrs.F_CODE = 'AL015';
-
-        // The FCODE for Buildings changed...
-        if (attrs.F_CODE == 'AL013') attrs.F_CODE = 'AL015';
-
-        // Tag changed
-        if (tags.vertical_obstruction_identifier)
+      case 'AP010': // Cart Track
+        if (attrs.WID && ! attrs.WD1)
         {
-            tags['aeroway:obstruction'] = tags.vertical_obstruction_identifier;
-            delete tags.vertical_obstruction_identifier;
+          attrs.WD1 = attrs.WID;
+          delete attrs.WID;
         }
+        break;
 
-        // Soil Surface Regions
-        if (! attrs.F_CODE)
+      case 'AP030': // Road
+        if (tags.bridge) attrs.LOC = '45'; // Above Surface
+        if (tags.tunnel) attrs.LOC = '40'; // Below Surface
+        if (tags.embankment || tags.man_made == 'causeway') attrs.LOC = '44'; // On Surface
+        if (attrs.RST == '6') attrs.RST = '2'; // Move 'ground' to 'unpaved'
+        if (attrs.WID && ! attrs.WD1)
         {
-            if (tags.surface)
-            {
-                attrs.F_CODE = 'DA010'; // Soil Surface Region
-                if (! tags.material)
-                {
-                    tags.material = tags.surface;
-                    delete tags.surface;
-                }
-            }
+          attrs.WD1 = attrs.WID;
+          delete attrs.WID;
+        }
+        break;
 
-            switch (tags.natural)
-            {
-                case 'mud':
-                    attrs.F_CODE = 'DA010'; // Soil Surface Region
-                    tags.material = tags.surface;
-                    break;
-
-                case 'sand':
-                    attrs.F_CODE = 'DA010'; // Soil Surface Region
-                    tags.material = tags.surface;
-                 break;
-
-                case 'bare_rock':
-                    attrs.F_CODE = 'DA010'; // Soil Surface Region
-                    tags.material = tags.surface;
-                    break;
-
-                case 'rock':
-                    attrs.F_CODE = 'DA010'; // Soil Surface Region
-                    tags.material = tags.surface;
-                    break;
-
-                case 'stone':
-                    attrs.F_CODE = 'DA010'; // Soil Surface Region
-                    tags.material = tags.surface;
-                    break;
-
-                case 'scree':
-                    attrs.F_CODE = 'DA010'; // Soil Surface Region
-                    tags.material = tags.surface;
-                    break;
-
-                case 'shingle':
-                    attrs.F_CODE = 'DA010'; // Soil Surface Region
-                    tags.material = tags.surface;
-                    break;
-            }
-        } // End ! F_CODE
+      case 'AP050': // Trail
+        // Apparently, AP050 (Trail) _doesn't_ have WTC=1 (All weather)
+        if (attrs.WTC == '1') attrs.WTC = '0';
+        break;
 
-        // Railway loading things
-        if (tags.railway == 'loading')
+      case 'AQ040': // Bridge
+        if (attrs.RST)
         {
-            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
+          // hoot.logWarn('Found RST = ' + attrsi.RST + ' in AQ040'); // Should not get this
+          var convSurf = { 1:5, 2:46, 5:104, 6:104, 8:104, 999:999 };
 
-        // We don't have BH220 in MGCP
-        switch (tags.man_made)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'water_works':
-                delete tags.man_made;
-                attrs.F_CODE = 'AL010'; // Facility
-                attrs.FFN = '350'; // Utilities
-                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.MCC = convSurf[attrs.RST];
+          delete attrs.RST;
         }
+        break;
 
-        // Names. Sometimes we don't have a name but we do have language ones
-        if (!tags.name) translate.swapName(tags);
-
-    }, // End applyToMgcpPreProcessing
+      case 'AQ140': // Vehicle Lot/Vehicle Storage area: Valid NFDD/NAS FCODE but not in the MGCP spec
+        if (geometryType == 'Point') attrs.F_CODE = 'AL015'; // Parking Garage Building
+        break;
 
+      case 'BA050': // Beach
+        if (attrs.MCC)
+        {
+          attrs.SMC = attrs.MCC;
+          delete attrs.MCC;
+        }
+        break;
 
-    applyToMgcpPostProcessing : function (tags, attrs, geometryType, notUsedTags)
-    {
-        // Gross generalisation. If we don't have an FCODE but we do have an FFN then we either have a
-        // Building or a Facility
-        // In the absence of any other info, we are making it a Building
-        if (!attrs.F_CODE)
+      case 'DA010': // Soil Surface Area
+        if (attrs.MCC)
         {
-            if (attrs.FFN) attrs.F_CODE = 'AL015';
+          attrs.SMC = attrs.MCC;
+          delete attrs.MCC;
         }
+        break;
 
-        // If we still don't have an FCODE, try a bit of brute force
-        if (!attrs.F_CODE)
+      case 'DB010': // Bluff/Cliff/Escarpment
+        if (attrs.MCC)
         {
-            var fcodeMap = {
-                'highway':'AP030','railway':'AN010','building':'AL015',
-                'ford':'BH070','waterway':'BH140','bridge':'AQ040','tomb':'AL036',
-                'railway:in_road':'AN010','tourism':'AL013','mine:access':'AA010',
-                'cutting':'DB070','shop':'AL015','office':'AL015'
-            };
-
-            for (var i in fcodeMap)
-            {
-                if (i in tags)
-                {
-                    // Debug
-                    // print('Added FCODE from Map: ' + fcodeMap[i]);
-                    attrs.F_CODE = fcodeMap[i];
-                    break;
-                }
-            }
-        } // End !fcode
+          attrs.SMC = attrs.MCC;
+          delete attrs.MCC;
+        }
+        break;
 
-        // Sort out the UID
-        if (tags.uuid)
+      case 'DB070': // Cut
+        if (attrs.MCC)
         {
-            var str = tags['uuid'].split(';');
-            attrs.UID = str[0].replace('{','').replace('}','');
+          attrs.SMC = attrs.MCC;
+          delete attrs.MCC;
         }
-        else if (tags['hoot:id'])
+        break;
+
+      case 'DB090': // Embankment
+        // If the embankment supports a transportation feature
+        if (tags.highway || tags.railway)
         {
-            attrs.UID = 'raw_id:' + tags['hoot:id'];
+          attrs.FIC = '2'; // Fill
         }
         else
         {
-            if (mgcp.configOut.OgrAddUuid == 'true') attrs.UID = createUuid().replace('{','').replace('}','');
+          attrs.FIC = '1'; // Mound
         }
+        break;
 
-        // Default railway
-        // if (attrs.F_CODE == 'AN010' && tags.railway == 'yes') attrs.RRC = '0';
-
-        // Not a great fit
-        if (tags.public_transportation == 'station' && tags.bus == 'yes') attrs.FFN = '481';
-
-        // Mapping Senior Citizens home to Accomodation. Not great
-        if (tags.amenity == 'social_facility' && tags['social_facility:for'] == 'senior') attrs.FFN = 550;
-
-        // These FCODES have "No prescribed attributes" in TRDv40
-        // Therefore:
-        // - clean up the the attrs
-        // - JSON the tags and stick them in a text field
-        var noAttrList = [ 'AJ010','AJ030','AK170','AL019','AL070','AL099','AN076',
-                           'BD100','BD180','BH165','BI010','BJ020','BJ031','BJ110',
-                           'DB061','DB071','DB100',
-                           'EA020','EA055','EC010','EC040','EC060',
-                           'FA090',
-                           'GB050',
-                           'ZD020',
-                         ];
-
-        if (noAttrList.indexOf(attrs.F_CODE) !== -1)
-        {
-            // The metadata tags to skip
-            var skipTags = ['F_CODE','ACC','TXT','UID','SDV','SDP','CCN','SRT'];
-            var tmpAttrs = {};
+      case 'EA010': // Crop Land
+        if (attrs.CSP == '15') attrs.F_CODE = 'EA040';
+        // hoot.logTrace('TRD3 feature EA010 changed to TRD4 EA040 - some data has been dropped');
+        break;
 
-            for (var i in attrs)
-            {
-                // Skip the metadata tags, save the others, then delete them
-                // from the main list of attrs
-                if (skipTags.indexOf(i) == -1)
-                {
-                    tmpAttrs[i] = attrs[i];
-                    delete attrs[i];
-                }
-            }
-            attrs.TEXT = JSON.stringify(tmpAttrs);
-        }
+      case 'ED030': // Mangrove Swamp
+        if (! attrs.TID) attrs.TID = '1001'; // Tidal
+        break;
 
-        // Add Weather Restrictions to transportation features
-        if (['AP010','AP030','AP050'].indexOf(attrs.FCODE > -1) && !attrs.WTC )
+      case 'AH050': // Fortification
+        if (tags['bunker_type'] == 'munitions')
         {
-            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.WTC = '1'; // All weather
-                    break;
-
-                case 'path':
-                case 'track':
-                    attrs.WTC = '2'; // Fair weather
-                    break;
-            }
-
-            // Use the road surface to possibly override the classification
-            // We are assumeing that unpaved roads are Fair Weather only
-            if (attrs.RST == '1') attrs.WTC = '1'; // Hard Paved surface
-            if (attrs.RST == '2') attrs.WTC = '2'; // Unpaved surface
+          attrs.F_CODE = 'AM060'; // Surface bunker
+          if (! attrs.PPO) attrs.PPO = '3'; // Ammunition
         }
 
-        // Additional rules for particular FCODE's
-        switch (attrs.F_CODE)
-        {
-            case undefined: // Break early if no value
-                break;
-
-            case 'AJ085': // Barn: Valid NFDD/NAS FCODE but not in the MGCP spec
-                attrs.F_CODE = 'AL015'; // Barns are Buildings
-                break;
-
-            case 'AK040': // Athletic Field, Sports Ground
-                if (attrs.MCC)
-                {
-                    attrs.SMC = attrs.MCC;
-                    delete attrs.MCC;
-                }
-                break;
-
-            case 'AL015': // General Building
-                // Unknown House of Worship
-                if (tags.amenity == 'place_of_worship' && tags.building == 'other') attrs.HWT = 999;
-
-                if (attrs.HWT && ! tags.amenity && ! attrs.FFN)
-                {
-                    attrs.FFN = '931';
-                }
-
-                if (attrs.FFN && (attrs.FFN !== '930' && attrs.FFN !== '931'))
-                {
-                    // Debug
-                    //print('AL015: Setting HWT 998');
-                    attrs.HWT = '998';
-                }
-
-                // This is a catch all and assigns "Commerce" as the function
-                // Exceptions to this are in the rules list
-                if (!attrs.FFN)
-                {
-                    if (tags.shop || tags.office) attrs.FFN = '440';
-                }
-                break;
-
-            case 'AL020': // Built-up Area
-                // Allowed values for FUC
-                if (['0','1','2','4','19','999'].indexOf(attrs['FUC']) < 0) attrs.FUC = '999';
-                break;
-
-            case 'AL105': // Settlement
-                // Allowed values for FUC
-                if (['0','4','8','19','999'].indexOf(attrs['FUC']) < 0) attrs.FUC = '999';
-                break;
-
-            case 'AN010': // Railway
-                if (tags.bridge) attrs.LOC = '45'; // Above Surface
-                if (tags.tunnel) attrs.LOC = '40'; // Below Surface
-                if (tags.embankment || tags.man_made == 'causeway') attrs.LOC = '44'; // On Surface
-
-                // Single lane roads dont have a median and are not separated
-                // NOTE: This could cause problems
-                if (attrs.LTN == '1')
-                {
-                    attrs.SEP = '1000';
-                    attrs.MES = '1000';
-                }
-                break;
-
-            case 'AP010': // Cart Track
-                if (attrs.WID && ! attrs.WD1)
-                {
-                    attrs.WD1 = attrs.WID;
-                    delete attrs.WID;
-                }
-                break;
-
-            case 'AP030': // Road
-                if (tags.bridge) attrs.LOC = '45'; // Above Surface
-                if (tags.tunnel) attrs.LOC = '40'; // Below Surface
-                if (tags.embankment || tags.man_made == 'causeway') attrs.LOC = '44'; // On Surface
-                if (attrs.RST == '6') attrs.RST = '2'; // Move 'ground' to 'unpaved'
-                if (attrs.WID && ! attrs.WD1)
-                {
-                    attrs.WD1 = attrs.WID;
-                    delete attrs.WID;
-                }
-                break;
-
-            case 'AP050': // Trail
-                // Apparently, AP050 (Trail) _doesn't_ have WTC=1 (All weather)
-                if (attrs.WTC == '1') attrs.WTC = '0';
-                break;
-
-            case 'AQ040': // Bridge
-                if (attrs.RST)
-                {
-                    // hoot.logWarn('Found RST = ' + attrsi.RST + ' in AQ040'); // Should not get this
-                    var convSurf = { 1:5, 2:46, 5:104, 6:104, 8:104, 999:999 };
-
-                    attrs.MCC = convSurf[attrs.RST];
-                    delete attrs.RST;
-                }
-                break;
-
-            case 'AQ140': // Vehicle Lot/Vehicle Storage area: Valid NFDD/NAS FCODE but not in the MGCP spec
-                if (geometryType == 'Point') attrs.F_CODE = 'AL015'; // Parking Garage Building
-                break;
-
-            case 'BA050': // Beach
-                if (attrs.MCC)
-                {
-                    attrs.SMC = attrs.MCC;
-                    delete attrs.MCC;
-                }
-                break;
-
-            case 'DA010': // Soil Surface Area
-                if (attrs.MCC)
-                {
-                    attrs.SMC = attrs.MCC;
-                    delete attrs.MCC;
-                }
-                break;
-
-            case 'DB010': // Bluff/Cliff/Escarpment
-                if (attrs.MCC)
-                {
-                    attrs.SMC = attrs.MCC;
-                    delete attrs.MCC;
-                }
-                break;
-
-            case 'DB070': // Cut
-                if (attrs.MCC)
-                {
-                    attrs.SMC = attrs.MCC;
-                    delete attrs.MCC;
-                }
-                break;
-
-            case 'DB090': // Embankment
-                // If the embankment supports a transportation feature
-                if (tags.highway || tags.railway)
-                {
-                    attrs.FIC = '2'; // Fill
-                }
-                else
-                {
-                    attrs.FIC = '1'; // Mound
-                }
-                break;
-
-            case 'EA010': // Crop Land
-                if (attrs.CSP == '15') attrs.F_CODE = 'EA040';
-                // hoot.logTrace('TRD3 feature EA010 changed to TRD4 EA040 - some data has been dropped');
-                break;
-
-            case 'ED030': // Mangrove Swamp
-                if (! attrs.TID) attrs.TID = '1001'; // Tidal
-                break;
-
-            case 'AH050': // Fortification
-                if (tags['bunker_type'] == 'munitions')
-                {
-                    attrs.F_CODE = 'AM060'; // Surface bunker
-                    if (! attrs.PPO) attrs.PPO = '3'; // Ammunition
-                }
-
-                // If we have a Castle, stop it being turned into a Bunker on import
-                if (tags.historic == 'castle') notUsedTags.historic = 'castle';
-                break;
-
-        } // End switch FCODE
-
-        if (mgcp.mgcpPostRules == undefined)
-        {
-            // "New" style complex rules
-            //
-            // Rules format:  ["test expression","output result"];
-            // Note: t = tags, a = attrs and attrs can only be on the RHS
-
-            // Most of these rules are to account for differences between MGCP & TDS/NFDD
-            // If we don't change it here, hoot tries to output the wrong FCODE
-            var rulesList = [
-            ["t.control_tower == 'yes'","a.F_CODE = 'AL241'"],
-            ["t.man_made == 'water_well'","a.F_CODE = 'AA050'"],
-            ["t.man_made == 'well'","a.F_CODE = 'AA050'"],
-            ["t.sport == 'tennis'","a.F_CODE = 'AK040'"],
-            ["t.natural == 'tree'","a.F_CODE = 'EC030'"],
-            ["t.amenity == 'ferry_terminal'","a.F_CODE = 'AQ125'; a.FFN = '7'"],
-            // ["t.landuse == 'forest'","a.F_CODE = 'EC030'"],
-            ["t.amenity == 'fuel'","a.F_CODE = 'AL015'"]
-            ];
-
-            mgcp.mgcpPostRules = translate.buildComplexRules(rulesList);
-        }
+        // If we have a Castle, stop it being turned into a Bunker on import
+        if (tags.historic == 'castle') notUsedTags.historic = 'castle';
+        break;
 
-        //translate.applyComplexRules(tags,attrs,mgcp.mgcpPostRules);
-        for (var i = 0, rLen = mgcp.mgcpPostRules.length; i < rLen; i++)
-        {
-            if (mgcp.mgcpPostRules[i][0](tags)) mgcp.mgcpPostRules[i][1](tags,attrs);
-        }
+    } // End switch FCODE
 
-        // Fix up SRT values so we comply with the spec. These values came from data files
-        // Format is: orig:new
-        srtFix = {
-            28:112,  // Ikonos Imagery -> Hires Commercial
-            66:112,  // Quickbird Multi 2.44m -> Hires Commercial
-            68:110,  // Quickbird Pan 60cm -> Very Hires Commercial
-        }; // End srtFix
-
-        if (attrs.SRT in srtFix) attrs.SRT = srtFix[attrs.SRT];
-
-        //Map alternate source date tags to SDV in order of precedence
-        //default in mgcp_rules is 'source:datetime'
-        if (! attrs.SDV)
-            attrs.SDV = tags['source:imagery:datetime']
-                || tags['source:date']
-                || tags['source:geometry:date']
-                || '';
-
-        //Map alternate source tags to ZI001_SDP in order of precedence
-        //default in mgcp_rules is 'source'
-        if (! attrs.SDP)
-            attrs.SDP = tags['source:imagery']
-                || tags['source:description']
-                || '';
-
-        // Chop the milliseconds off the "source:datetime"
-        if (attrs.SDV)
-        {
-            attrs.SDV = translate.chopDateTime(attrs.SDV);
-        }
+    if (mgcp.mgcpPostRules == undefined)
+    {
+      // "New" style complex rules
+      //
+      // Rules format:  ["test expression","output result"];
+      // Note: t = tags, a = attrs and attrs can only be on the RHS
+
+      // Most of these rules are to account for differences between MGCP & TDS/NFDD
+      // If we don't change it here, hoot tries to output the wrong FCODE
+      var rulesList = [
+      ["t.control_tower == 'yes'","a.F_CODE = 'AL241'"],
+      ["t.man_made == 'water_well'","a.F_CODE = 'AA050'"],
+      ["t.man_made == 'well'","a.F_CODE = 'AA050'"],
+      ["t.sport == 'tennis'","a.F_CODE = 'AK040'"],
+      ["t.natural == 'tree'","a.F_CODE = 'EC030'"],
+      ["t.amenity == 'ferry_terminal'","a.F_CODE = 'AQ125'; a.FFN = '7'"],
+      // ["t.landuse == 'forest'","a.F_CODE = 'EC030'"],
+      ["t.amenity == 'fuel'","a.F_CODE = 'AL015'"]
+      ];
+
+      mgcp.mgcpPostRules = translate.buildComplexRules(rulesList);
+    }
+
+    //translate.applyComplexRules(tags,attrs,mgcp.mgcpPostRules);
+    for (var i = 0, rLen = mgcp.mgcpPostRules.length; i < rLen; i++)
+    {
+      if (mgcp.mgcpPostRules[i][0](tags)) mgcp.mgcpPostRules[i][1](tags,attrs);
+    }
+
+    // Fix up SRT values so we comply with the spec. These values came from data files
+    // Format is: orig:new
+    srtFix = {
+      28:112,  // Ikonos Imagery -> Hires Commercial
+      66:112,  // Quickbird Multi 2.44m -> Hires Commercial
+      68:110,  // Quickbird Pan 60cm -> Very Hires Commercial
+    }; // End srtFix
+
+    if (attrs.SRT in srtFix) attrs.SRT = srtFix[attrs.SRT];
+
+    //Map alternate source date tags to SDV in order of precedence
+    //default in mgcp_rules is 'source:datetime'
+    if (! attrs.SDV)
+      attrs.SDV = tags['source:imagery:datetime']
+        || tags['source:date']
+        || tags['source:geometry:date']
+        || '';
+
+    //Map alternate source tags to ZI001_SDP in order of precedence
+    //default in mgcp_rules is 'source'
+    if (! attrs.SDP)
+      attrs.SDP = tags['source:imagery']
+        || tags['source:description']
+        || '';
+
+    // Chop the milliseconds off the "source:datetime"
+    if (attrs.SDV)
+    {
+      attrs.SDV = translate.chopDateTime(attrs.SDV);
+    }
+
+  }, // End of applyToMgcpPostProcessing
 
-    }, // End of applyToMgcpPostProcessing
+  // ##### End of the xxToMgcpxx Block #####
 
-    // ##### End of the xxToMgcpxx Block #####
+  // toOsm - Translate Attrs to Tags
+  toOsm : function(attrs, layerName, geometryType)
+  {
+    tags = {};  // This is the output
 
-    // toOsm - Translate Attrs to Tags
-    toOsm : function(attrs, layerName, geometryType)
+    // 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 (mgcp.configIn == undefined)
     {
-        tags = {};  // This is the output
+      mgcp.configIn = {};
+      mgcp.configIn.OgrDebugAddfcode = config.getOgrDebugAddfcode();
+      mgcp.configIn.OgrDebugDumptags = config.getOgrDebugDumptags();
+      mgcp.configIn.OgrAddUuid = config.getOgrAddUuid();
 
-        // 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 (mgcp.configIn == undefined)
-        {
-            mgcp.configIn = {};
-            mgcp.configIn.OgrDebugAddfcode = config.getOgrDebugAddfcode();
-            mgcp.configIn.OgrDebugDumptags = config.getOgrDebugDumptags();
-            mgcp.configIn.OgrAddUuid = config.getOgrAddUuid();
+      // Get any changes
+      mgcp.toChange = hoot.Settings.get("schema.translation.override");
+    }
 
-            // Get any changes
-            mgcp.toChange = hoot.Settings.get("schema.translation.override");
-        }
+    // Debug:
+    if (mgcp.configIn.OgrDebugDumptags == 'true') translate.debugOutput(attrs,layerName,geometryType,'','In Attrs: ');
 
-        // Debug:
-        if (mgcp.configIn.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);
 
-        // See if we have an o2s_X layer and try to unpack it
-        if (layerName.indexOf('o2s_') > -1)
-        {
-            tags = translate.parseO2S(attrs);
+      // Add some metadata
+      if (! tags.uuid)
+      {
+        if (mgcp.configIn.OgrAddUuid == 'true') tags.uuid = createUuid();
+      }
 
-            // Add some metadata
-            if (! tags.uuid)
-            {
-                if (mgcp.configIn.OgrAddUuid == 'true') tags.uuid = createUuid();
-            }
+      if (! tags.source) tags.source = 'mgcp:' + layerName.toLowerCase();
 
-            if (! tags.source) tags.source = 'mgcp:' + layerName.toLowerCase();
+      // Debug:
+      if (mgcp.configIn.OgrDebugDumptags == 'true')
+      {
+        translate.debugOutput(tags,layerName,geometryType,'','Out tags: ');
+        print('');
+      }
 
-            // Debug:
-            if (mgcp.configIn.OgrDebugDumptags == 'true')
-            {
-                translate.debugOutput(tags,layerName,geometryType,'','Out tags: ');
-                print('');
-            }
 
+      return tags;
+    } // End layername = o2s_X
 
-            return tags;
-        } // End layername = o2s_X
+    // Set up the fcode translation rules
+    if (mgcp.fcodeLookup == undefined)
+    {
+      // Order is important:
+      // First the MGCPv3 & 4 FCODES, then the common ones. This ensures that the common ones don't
+      // stomp on the other ones
+      mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,mgcp.rules.fcodeOne2oneInV3);
+      mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,fcodeCommon.one2one);
 
-        // Set up the fcode translation rules
-        if (mgcp.fcodeLookup == undefined)
-        {
-            // Order is important:
-            // First the MGCPv3 & 4 FCODES, then the common ones. This ensures that the common ones don't
-            // stomp on the other ones
-            mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,mgcp.rules.fcodeOne2oneInV3);
-            mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,fcodeCommon.one2one);
+      mgcp.fcodeLookup = translate.createLookup(mgcp.rules.fcodeOne2oneV4);
 
-            mgcp.fcodeLookup = translate.createLookup(mgcp.rules.fcodeOne2oneV4);
+      // Debug:
+      // translate.dumpOne2OneLookup(mgcp.fcodeLookup);
+    }
 
-            // Debug:
-            // translate.dumpOne2OneLookup(mgcp.fcodeLookup);
-        }
+    if (mgcp.lookup == undefined)
+    {
+      // Setup lookup tables to make translation easier
 
-        if (mgcp.lookup == undefined)
-        {
-            // Setup lookup tables to make translation easier
+      // Add the MGCPv3.0 specific attributes to the v4.0/common attribute table
+      mgcp.rules.one2one.push.apply(mgcp.rules.one2one,mgcp.rules.one2oneIn);
 
-            // Add the MGCPv3.0 specific attributes to the v4.0/common attribute table
-            mgcp.rules.one2one.push.apply(mgcp.rules.one2one,mgcp.rules.one2oneIn);
+      mgcp.lookup = translate.createLookup(mgcp.rules.one2one);
+    }
 
-            mgcp.lookup = translate.createLookup(mgcp.rules.one2one);
-        }
+    if (mgcp.txtLookup == undefined)
+    {
+      mgcp.txtLookup = {};
+      // Add the MGCPv3.0 specific attributes to the v4.0/common attribute table
+      for (var i in mgcp.rules.txtBiased) mgcp.txtLookup[i] = mgcp.rules.txtBiased[i];
+      for (var i in mgcp.rules.txtBiasedV3) mgcp.txtLookup[i] = mgcp.rules.txtBiasedV3[i];
+    }
 
-        if (mgcp.txtLookup == undefined)
-        {
-            mgcp.txtLookup = {};
-            // Add the MGCPv3.0 specific attributes to the v4.0/common attribute table
-            for (var i in mgcp.rules.txtBiased) mgcp.txtLookup[i] = mgcp.rules.txtBiased[i];
-            for (var i in mgcp.rules.txtBiasedV3) mgcp.txtLookup[i] = mgcp.rules.txtBiasedV3[i];
-        }
+    if (mgcp.numLookup == undefined)
+    {
+      mgcp.numLookup = {};
+      for (var i in mgcp.rules.numBiased) mgcp.numLookup[i] = mgcp.rules.numBiased[i];
+      for (var i in mgcp.rules.numBiasedV3) mgcp.numLookup[i] = mgcp.rules.numBiasedV3[i];
+    }
 
-        if (mgcp.numLookup == undefined)
-        {
-            mgcp.numLookup = {};
-            for (var i in mgcp.rules.numBiased) mgcp.numLookup[i] = mgcp.rules.numBiased[i];
-            for (var i in mgcp.rules.numBiasedV3) mgcp.numLookup[i] = mgcp.rules.numBiasedV3[i];
-        }
 
+    // A little cleaning before we try to untangle stuff
+    delete attrs.SHAPE_Length;
+    delete attrs.SHAPE_Area;
 
-        // A little cleaning before we try to untangle stuff
-        delete attrs.SHAPE_Length;
-        delete attrs.SHAPE_Area;
+    // Untangle MGCP attributes & OSM tags
+    // NOTE: This could get wrapped with an ENV variable so it only gets called during import
+    mgcp.untangleAttributes(attrs, tags);
+
+    // Debug:
+    if (mgcp.configIn.OgrDebugDumptags == 'true')
+    {
+      translate.debugOutput(attrs,layerName,geometryType,'','Untangle attrs: ');
+      translate.debugOutput(tags,layerName,geometryType,'','Untangle tags: ');
+    }
 
-        // Untangle MGCP attributes & OSM tags
-        // NOTE: This could get wrapped with an ENV variable so it only gets called during import
-        mgcp.untangleAttributes(attrs, tags);
+    // pre processing
+    mgcp.applyToOsmPreProcessing(attrs, layerName, geometryType);
 
-        // Debug:
-        if (mgcp.configIn.OgrDebugDumptags == 'true')
+    // Use the FCODE to add some tags
+    if (attrs.F_CODE)
+    {
+      var ftag = mgcp.fcodeLookup['F_CODE'][attrs.F_CODE];
+      if (ftag)
+      {
+        if (!tags[ftag[0]])
         {
-            translate.debugOutput(attrs,layerName,geometryType,'','Untangle attrs: ');
-            translate.debugOutput(tags,layerName,geometryType,'','Untangle tags: ');
+          tags[ftag[0]] = ftag[1];
         }
-
-        // pre processing
-        mgcp.applyToOsmPreProcessing(attrs, layerName, geometryType);
-
-        // Use the FCODE to add some tags
-        if (attrs.F_CODE)
+        else
         {
-            var ftag = mgcp.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 FCODE ' + attrs.F_CODE + ' not found');
-            }
+          // Debug
+          print('Tried to replace: ' + ftag[0] + '=' + tags[ftag[0]] + '  with ' + 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 FCODE ' + 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;
+    // 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;
 
-        // 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, mgcp.numLookup,'forward',[]);
-        translate.applySimpleTxtBiased(notUsedAttrs, tags,  mgcp.txtLookup,'forward');
+    // 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, mgcp.numLookup);
+    translate.txtToOSM(notUsedAttrs, tags,  mgcp.txtLookup);
 
-        // one 2 one
-        translate.applyOne2One(notUsedAttrs, tags, mgcp.lookup, {'k':'v'});
+    // one 2 one
+    translate.applyOne2One(notUsedAttrs, tags, mgcp.lookup, {'k':'v'},[]);
 
-        // post processing
-        mgcp.applyToOsmPostProcessing(attrs, tags, layerName, geometryType);
+    // post processing
+    mgcp.applyToOsmPostProcessing(attrs, tags, layerName, geometryType);
 
-        // Debug: Add the FCODE to the tags
-        if (mgcp.configIn.OgrDebugAddfcode == 'true') tags['raw:debugFcode'] = attrs.F_CODE;
+    // Debug: Add the FCODE to the tags
+    if (mgcp.configIn.OgrDebugAddfcode == 'true') tags['raw:debugFcode'] = attrs.F_CODE;
 
-        // Override tag values if appropriate
-        translate.overrideValues(tags,mgcp.toChange);
+    // Override tag values if appropriate
+    translate.overrideValues(tags,mgcp.toChange);
 
-        // Debug:
-        if (mgcp.configIn.OgrDebugDumptags == 'true')
-        {
-            translate.debugOutput(notUsedAttrs,layerName,geometryType,'','Not used: ');
-            translate.debugOutput(tags,layerName,geometryType,'','Out tags: ');
-            print('');
-        }
+    // Debug:
+    if (mgcp.configIn.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
+  toMgcp : function(tags, elementType, geometryType)
+  {
+    var tableName = '';
+    var returnData = []; // The array of features to return
+    var transMap = {}; // A map of translated attributes
+    attrs = {}; // This is the output <GLOBAL>
+    attrs.F_CODE = '';
+
+    // 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 (mgcp.configOut == undefined)
+    {
+      mgcp.configOut = {};
+      mgcp.configOut.OgrDebugDumptags = config.getOgrDebugDumptags();
+      mgcp.configOut.OgrNoteExtra = config.getOgrNoteExtra();
+      mgcp.configOut.OgrSplitO2s = config.getOgrSplitO2s();
+      mgcp.configOut.OgrThrowError = config.getOgrThrowError();
+      mgcp.configOut.OgrAddUuid = config.getOgrAddUuid();
+
+      // Get any changes to OSM tags
+      // NOTE: the rest of the config variables will change to this style of assignment soon
+      mgcp.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 (mgcp.rawSchema == undefined)
+    {
+      var tmp_schema = mgcp.getDbSchema();
+    }
 
-        return tags;
-    }, // End of ToOsm
+    // The Nuke Option: If we have a relation, drop the feature and carry on
+    if (tags['building:part']) return null;
 
-    // This gets called by translateToOGR and is where the main work gets done
-    // We get Tags and return Attrs and a tableName
-    toMgcp : function(tags, elementType, geometryType)
-    {
-        var tableName = '';
-        var returnData = []; // The array of features to return
-        attrs = {}; // This is the output <GLOBAL>
-        attrs.F_CODE = '';
+    // The Nuke Option: "Collections" are groups of different feature types: Point, Area and Line
+    // There is no way we can translate these to a single TDS feature
+    if (geometryType == 'Collection') return null;
 
-        // 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 (mgcp.configOut == undefined)
-        {
-            mgcp.configOut = {};
-            mgcp.configOut.OgrDebugDumptags = config.getOgrDebugDumptags();
-            mgcp.configOut.OgrNoteExtra = config.getOgrNoteExtra();
-            mgcp.configOut.OgrSplitO2s = config.getOgrSplitO2s();
-            mgcp.configOut.OgrThrowError = config.getOgrThrowError();
-            mgcp.configOut.OgrAddUuid = config.getOgrAddUuid();
-
-            // Get any changes to OSM tags
-            // NOTE: the rest of the config variables will change to this style of assignment soon
-            mgcp.toChange = hoot.Settings.get("schema.translation.override");
-        }
+    // Debug:
+    if (mgcp.configOut.OgrDebugDumptags == 'true') translate.debugOutput(tags,'',geometryType,elementType,'In tags: ');
 
-        // Check if we have a schema. This is a quick way to workout if various lookup tables have been built
-        if (mgcp.rawSchema == undefined)
-        {
-            var tmp_schema = mgcp.getDbSchema();
-        }
+    // Set up the fcode translation rules
+    if (mgcp.fcodeLookup == undefined)
+    {
+      // Order is important:
+      // First the MGCPv4 FCODES, then the common ones. This ensures that the common ones don't
+      // stomp on the V4 ones
+      mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,mgcp.rules.fcodeOne2oneOut);
+      mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,fcodeCommon.one2one);
 
-        // The Nuke Option: If we have a relation, drop the feature and carry on
-        if (tags['building:part']) return null;
+      mgcp.fcodeLookup = translate.createBackwardsLookup(mgcp.rules.fcodeOne2oneV4);
 
-        // The Nuke Option: "Collections" are groups of different feature types: Point, Area and Line
-        // There is no way we can translate these to a single TDS feature
-        if (geometryType == 'Collection') return null;
+      // Debug
+      // translate.dumpOne2OneLookup(mgcp.fcodeLookup);
+    }
 
-        // Debug:
-        if (mgcp.configOut.OgrDebugDumptags == 'true') translate.debugOutput(tags,'',geometryType,elementType,'In tags: ');
+    if (mgcp.lookup == undefined)
+    {
+      // Add the conversion from MGCPv3.0 attributes to the v4.0/common attribute table
+      mgcp.rules.one2one.push.apply(mgcp.rules.one2one,mgcp.rules.one2oneOut);
 
-        // Set up the fcode translation rules
-        if (mgcp.fcodeLookup == undefined)
-        {
-            // Order is important:
-            // First the MGCPv4 FCODES, then the common ones. This ensures that the common ones don't
-            // stomp on the V4 ones
-            mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,mgcp.rules.fcodeOne2oneOut);
-            mgcp.rules.fcodeOne2oneV4.push.apply(mgcp.rules.fcodeOne2oneV4,fcodeCommon.one2one);
+      mgcp.lookup = translate.createBackwardsLookup(mgcp.rules.one2one);
 
-            mgcp.fcodeLookup = translate.createBackwardsLookup(mgcp.rules.fcodeOne2oneV4);
+      // Debug
+      // translate.dumpOne2OneLookup(mgcp.lookup);
+    }
 
-            // Debug
-            // translate.dumpOne2OneLookup(mgcp.fcodeLookup);
-        }
+    // Override values if appropriate
+    translate.overrideValues(tags,mgcp.toChange);
 
-        if (mgcp.lookup == undefined)
-        {
-            // Add the conversion from MGCPv3.0 attributes to the v4.0/common attribute table
-            mgcp.rules.one2one.push.apply(mgcp.rules.one2one,mgcp.rules.one2oneOut);
+    // pre processing
+    mgcp.applyToMgcpPreProcessing(tags, attrs, geometryType);
 
-            mgcp.lookup = translate.createBackwardsLookup(mgcp.rules.one2one);
+    // 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)));
 
-            // Debug
-            // translate.dumpOne2OneLookup(mgcp.lookup);
-        }
+    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'];
 
-        // Override values if appropriate
-        translate.overrideValues(tags,mgcp.toChange);
+    // apply the simple number and text biased rules
+    translate.numToOgr(attrs, notUsedTags, mgcp.rules.numBiased,mgcp.rules.intList,transMap);
+    translate.txtToOgr(attrs, notUsedTags,  mgcp.rules.txtBiased,transMap);
 
-        // pre processing
-        mgcp.applyToMgcpPreProcessing(tags, attrs, geometryType);
+    // one 2 one
+    translate.applyOne2One(notUsedTags, attrs, mgcp.lookup, mgcp.fcodeLookup, transMap);
 
-        // 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)));
+    // post processing
+    // mgcp.applyToMgcpPostProcessing(attrs, tableName, geometryType);
+    mgcp.applyToMgcpPostProcessing(tags, attrs, geometryType, notUsedTags);
 
-        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'];
+    // Debug
+    if (mgcp.configOut.OgrDebugDumptags == 'true') translate.debugOutput(notUsedTags,'',geometryType,elementType,'Not used: ');
 
-        // apply the simple number and text biased rules
-        translate.applySimpleNumBiased(attrs, notUsedTags, mgcp.rules.numBiased,'backward',mgcp.rules.intList);
-        translate.applySimpleTxtBiased(attrs, notUsedTags,  mgcp.rules.txtBiased,'backward');
+    // If we have unused tags, add them to the TXT field
+    // NOTE: We are not checking if this is longer than 255 characters
+    if (Object.keys(notUsedTags).length > 0 && mgcp.configOut.OgrNoteExtra == 'attribute')
+    {
+      var tStr = '<OSM>' + JSON.stringify(notUsedTags) + '</OSM>';
+      attrs.TXT = translate.appendValue(attrs.TXT,tStr,';');
+    }
+
+    // Set the tablename: [P,A,L]<fcode>
+    // tableName = geometryType.toString().substring(0,1) + attrs.F_CODE;
+    tableName = geometryType.toString().charAt(0) + attrs.F_CODE;
+
+    // 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 the o2s layer
+    if (mgcp.layerNameLookup[tableName])
+    {
+      // Check if we need to return more than one feature
+      // NOTE: This returns structure we are going to send back to Hoot:  {attrs: attrs, tableName: 'Name'}
+      returnData = mgcp.manyFeatures(geometryType,tags,attrs,transMap);
 
-        // one 2 one
-        translate.applyOne2One(notUsedTags, attrs, mgcp.lookup, mgcp.fcodeLookup);
+      // Now go through the features and clean them up
+      var gType = geometryType.toString().charAt(0);
 
-        // post processing
-        // mgcp.applyToMgcpPostProcessing(attrs, tableName, geometryType);
-        mgcp.applyToMgcpPostProcessing(tags, attrs, geometryType, notUsedTags);
+      for (var i = 0, fLen = returnData.length; i < fLen; i++)
+      {
+        returnData[i]['attrs']['FCODE'] = returnData[i]['attrs']['F_CODE'];
+        delete returnData[i]['attrs']['F_CODE'];
 
-        // Debug
-        if (mgcp.configOut.OgrDebugDumptags == 'true') translate.debugOutput(notUsedTags,'',geometryType,elementType,'Not used: ');
+        // Now make sure that we have a valid feature _before_ trying to validate and jam it into the list of
+        // features to return
+        var gFcode = gType + returnData[i]['attrs']['FCODE'];
+        if (mgcp.layerNameLookup[gFcode.toUpperCase()])
+        {
+          // Validate attrs: remove all that are not supposed to be part of a feature
+          mgcp.validateAttrs(geometryType,returnData[i]['attrs']);
 
-        // If we have unused tags, add them to the TXT field
-        // NOTE: We are not checking if this is longer than 255 characters
-        if (Object.keys(notUsedTags).length > 0 && mgcp.configOut.OgrNoteExtra == 'attribute')
+          returnData[i]['tableName'] = mgcp.layerNameLookup[gFcode.toUpperCase()];
+        }
+        else
         {
-            var tStr = '<OSM>' + JSON.stringify(notUsedTags) + '</OSM>';
-            attrs.TXT = translate.appendValue(attrs.TXT,tStr,';');
+          // Debug
+          // print('## Skipping: ' + gFcode);
+          returnData.splice(i,1);
         }
+      } // End returnData loop
 
-        // Set the tablename: [P,A,L]<fcode>
-        // tableName = geometryType.toString().substring(0,1) + attrs.F_CODE;
-        tableName = geometryType.toString().charAt(0) + attrs.F_CODE;
+      // If we have unused tags, throw them into the "extra" layer
+      if (Object.keys(notUsedTags).length > 0 && mgcp.configOut.OgrNoteExtra == 'file')
+      {
+        var extraFeature = {};
+        extraFeature.tags = JSON.stringify(notUsedTags);
+        extraFeature.uuid = attrs.UID;
 
-        // 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 the o2s layer
-        if (mgcp.layerNameLookup[tableName])
-        {
-            // Check if we need to return more than one feature
-            // NOTE: This returns structure we are going to send back to Hoot:  {attrs: attrs, tableName: 'Name'}
-            returnData = mgcp.manyFeatures(geometryType,tags,attrs);
+        var extraName = 'extra_' + geometryType.toString().charAt(0);
 
-            // Now go through the features and clean them up
-            var gType = geometryType.toString().charAt(0);
+        returnData.push({attrs: extraFeature, tableName: extraName});
+      } // End notUsedTags
 
-            for (var i = 0, fLen = returnData.length; i < fLen; i++)
-            {
-                returnData[i]['attrs']['FCODE'] = returnData[i]['attrs']['F_CODE'];
-                delete returnData[i]['attrs']['F_CODE'];
-
-                // Now make sure that we have a valid feature _before_ trying to validate and jam it into the list of
-                // features to return
-                var gFcode = gType + returnData[i]['attrs']['FCODE'];
-                if (mgcp.layerNameLookup[gFcode.toUpperCase()])
-                {
-                    // Validate attrs: remove all that are not supposed to be part of a feature
-                    mgcp.validateAttrs(geometryType,returnData[i]['attrs']);
-
-                    returnData[i]['tableName'] = mgcp.layerNameLookup[gFcode.toUpperCase()];
-                }
-                else
-                {
-                    // 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 && mgcp.configOut.OgrNoteExtra == 'file')
-            {
-                var extraFeature = {};
-                extraFeature.tags = JSON.stringify(notUsedTags);
-                extraFeature.uuid = attrs.UID;
-
-                var extraName = 'extra_' + geometryType.toString().charAt(0);
+      // Look for Review tags and push them to a review layer if found
+      if (tags['hoot:review:needs'] == 'yes')
+      {
+        var reviewAttrs = {};
 
-                returnData.push({attrs: extraFeature, tableName: extraName});
-            } // End notUsedTags
+        // 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'];
 
-            // 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
-        } // End else We have a feature
-        else // We DON'T have a feature
+        var reviewTable = 'review_' + geometryType.toString().charAt(0);
+        returnData.push({attrs: reviewAttrs, tableName: reviewTable});
+      } // End ReviewTags
+    } // End else We have a feature
+    else // We DON'T have a feature
+    {
+      // For the UI: Throw an error and die if we don't have a valid feature
+      if (mgcp.configOut.OgrThrowError == 'true')
+      {
+        if (! attrs.F_CODE)
         {
-            // For the UI: Throw an error and die if we don't have a valid feature
-            if (mgcp.configOut.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 MGCP TRD4'}, tableName: ''});
-                    return returnData;
-                }
-            }
+          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 MGCP TRD4'}, tableName: ''});
+          return returnData;
+        }
+      }
 
-            hoot.logTrace('FCODE and Geometry: ' + tableName + ' is not in the schema');
+      hoot.logTrace('FCODE and Geometry: ' + tableName + ' is not in the schema');
 
-            tableName = 'o2s_' + geometryType.toString().charAt(0);
+      tableName = 'o2s_' + geometryType.toString().charAt(0);
 
-            // Debug:
-            // Dump out what attributes we have converted before they get wiped out
-            if (mgcp.configOut.OgrDebugDumptags == 'true') translate.debugOutput(attrs,'',geometryType,elementType,'Converted attrs: ');
+      // Debug:
+      // Dump out what attributes we have converted before they get wiped out
+      if (mgcp.configOut.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'];
+      // We want to keep the hoot:id if present
+      if (tags['hoot:id']) tags.raw_id = tags['hoot:id'];
 
-            for (var i in tags)
-            {
-                // Clean out all of the "source:XXX" tags to save space
-                if (i.indexOf('source:') !== -1) delete tags[i];
-                if (i.indexOf('error:') !== -1) delete tags[i];
-                if (i.indexOf('hoot:') !== -1) delete tags[i];
-            }
+      for (var i in tags)
+      {
+        // Clean out all of the "source:XXX" tags to save space
+        if (i.indexOf('source:') !== -1) delete tags[i];
+        if (i.indexOf('error:') !== -1) delete tags[i];
+        if (i.indexOf('hoot:') !== -1) delete tags[i];
+      }
 
-            // var str = JSON.stringify(tags);
-            var str = JSON.stringify(tags,Object.keys(tags).sort());
+      // var str = JSON.stringify(tags);
+      var str = JSON.stringify(tags,Object.keys(tags).sort());
 
-            // 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 || mgcp.configOut.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);
-                }
-
-                // Now split the text across the available tags
-                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
-
-        // Debug:
-        if (mgcp.configOut.OgrDebugDumptags == 'true')
+      // 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 || mgcp.configOut.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 (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('');
+          hoot.logTrace('o2s tags truncated to fit in available space.');
+          str = str.substring(0,1012);
         }
 
-        return returnData;
+        // Now split the text across the available tags
+        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
+
+    // Debug:
+    if (mgcp.configOut.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;
 
-    } // End of toMgcp
+  } // End of toMgcp
 
 } // End of mgcp
Clone this wiki locally