diff --git a/nmpfilter/README.md b/nmpfilter/README.md
index 05fd3d99..ae670548 100644
--- a/nmpfilter/README.md
+++ b/nmpfilter/README.md
@@ -1,3 +1,18 @@
+# nmpbycountry
+
+**Purpose:**
+Splits *tm-master.nmp* into smaller .nmp files filtered by country.
+
+**usage:**
+`nmpbycountry RgCsvFile MasterNMP OutputDir`, where
+* `RgCsvFile` is the path of *regions.csv*
+* `MasterNMP` is the path of *tm-master.nmp*
+* `OutputDir` is the directory in which to write the resulting .nmp files. Trailing slash required.
+
+The output directory should be empty before running *nmpbycountry*. Files for countries without NMPs will not be written. Thus any existing file for a country that subsequently has all NMPs removed will not be overwritten.
+
+---
+
# nmpbyregion
**Purpose:**
@@ -8,3 +23,23 @@ Splits *tm-master.nmp* into smaller .nmp files filtered by region.
* `HwyDataDir` is the path of the *hwy_data* directory in the HighwayData repository, or equivalent
* `MasterNMP` is the path of *tm-master.nmp*
* `OutputDir` is the directory in which to write the resulting .nmp files. Trailing slash required.
+
+---
+
+# nmpfpfilter
+
+**Purpose:**
+Removes marked false-positive pairs from a specified .nmp file.
+
+**usage:**
+`nmpfpfilter InputFile OutputFile`
+
+---
+
+# nmplifilter
+
+**Purpose:**
+Removes all NMP pairs from a specified .nmp file except those that look intentional.
+
+**usage:**
+`nmplifilter InputFile OutputFile`
diff --git a/nmpfilter/nmpbycountry.cpp b/nmpfilter/nmpbycountry.cpp
new file mode 100644
index 00000000..09290a28
--- /dev/null
+++ b/nmpfilter/nmpbycountry.cpp
@@ -0,0 +1,109 @@
+// Travel Mapping Project, Eric Bryant, 2018
+#include
+#include
+#include
+#include
+#include
+using namespace std;
+
+class region
+{ public:
+ string code, name, country, continent, regionType;
+
+ void UseRootRg()
+ { // Remove dashes from code
+ for (int dash = code.find('-'); dash > 0; dash = code.find('-'))
+ code.erase(dash, 1);
+ // Convert to lower case
+ for (unsigned int i = 0; i < code.size(); i++)
+ if (code[i] >= 'A' && code[i] <= 'Z')
+ code[i] += 32;
+ }
+
+ region (string &CSVline)
+ { unsigned int i = 0;
+ while (i < CSVline.size() && CSVline[i] != ';') { code.push_back(CSVline[i]); i++; } i++;
+ while (i < CSVline.size() && CSVline[i] != ';') { name.push_back(CSVline[i]); i++; } i++;
+ while (i < CSVline.size() && CSVline[i] != ';') { country.push_back(CSVline[i]); i++; } i++;
+ while (i < CSVline.size() && CSVline[i] != ';') { continent.push_back(CSVline[i]); i++; } i++;
+ while (i < CSVline.size() && CSVline[i] != ';') { regionType.push_back(CSVline[i]); i++; } i++;
+ UseRootRg();
+ }
+};
+
+bool GetRegions(char *CsvFile, list &CoList, vector &RgList)
+// Read contents of regions.csv
+{ ifstream CSV(CsvFile);
+ if (!CSV) { cout << CsvFile << " not found!\n"; return 0;}
+
+ string CSVline;
+ getline(CSV, CSVline); // Skip header row
+ while (getline(CSV, CSVline))
+ { RgList.emplace_back(CSVline);
+ CoList.emplace_back(RgList.back().country);
+ }
+ CoList.sort();
+ list::iterator c = CoList.begin();
+ list::iterator d = CoList.begin(); d++;
+ while (d != CoList.end()) // step thru each country in list
+ { while (*c == *d) d = CoList.erase(d); // remove duplicates
+ c++; d++;
+ }
+ return 1;
+}
+
+bool ReadMaster(char *MasterNMP, vector &master)
+// Read contents of tm-master.nmp
+{ ifstream CSV(MasterNMP);
+ if (!CSV)
+ { std::cout << MasterNMP << " not found\n";
+ return 0;
+ }
+ string CSVline;
+ while (getline(CSV, CSVline)) master.emplace_back(CSVline);
+ return 1;
+}
+
+bool RgMatch(vector &RgList, string &co, string rg)
+// Find region code in RgList vector; check whether it matches country
+{ for (unsigned int r = 0; r < RgList.size(); r++)
+ { if (RgList[r].code == rg)
+ if (RgList[r].country == co) return 1;
+ else return 0;
+ }
+ return 0;
+}
+
+void filter(list &CoList, vector &RgList, vector &master, char *OutputDir)
+// Write .nmp files for each country
+{ for (list::iterator c = CoList.begin(); c != CoList.end(); c++)
+ { vector output;
+ for (unsigned int l = 1; l < master.size(); l+=2)
+ { if ( RgMatch(RgList, *c, master[l-1].substr(0, master[l-1].find('.'))) \
+ || RgMatch(RgList, *c, master[l].substr(0, master[l].find('.'))) )
+ { output.emplace_back(master[l-1]);
+ output.emplace_back(master[l]);
+ }
+ }
+ //cout << "output vector created: " << output.size() << " lines.\n";
+ if (output.size())
+ { string outfile = OutputDir+*c+".nmp";
+ ofstream nmp(outfile.data());
+ for (unsigned int l = 0; l < output.size(); l++)
+ nmp << output[l] << '\n';
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{ if (argc != 4)
+ { cout << "usage: nmpbycountry RgCsvFile MasterNMP OutputDir\n";
+ return 0;
+ }
+ list CoList;
+ vector RgList;
+ vector master;
+ if (!GetRegions(argv[1], CoList, RgList)) return 0;
+ if (!ReadMaster(argv[2], master)) return 0;
+ filter(CoList, RgList, master, argv[3]);
+}
diff --git a/nmpfilter/nmpfpfilter.cpp b/nmpfilter/nmpfpfilter.cpp
new file mode 100644
index 00000000..f682407a
--- /dev/null
+++ b/nmpfilter/nmpfpfilter.cpp
@@ -0,0 +1,23 @@
+// Travel Mapping Project, Eric Bryant, 2018
+#include
+#include
+#include
+using namespace std;
+
+int main(int argc, char *argv[])
+{ if (argc != 3)
+ { cout << "usage: nmpfpfilter InputFile OutputFile\n";
+ return 0;
+ }
+ ifstream in(argv[1]);
+ if (!in)
+ { std::cout << argv[1] << " not found\n";
+ return 0;
+ }
+ string pt1, pt2;
+ ofstream out(argv[2]);
+ while (getline(in, pt1) && getline(in, pt2))
+ { if (pt1.substr(pt1.find_last_of(' ')+1, 2) != "FP" || pt2.substr(pt2.find_last_of(' ')+1, 2) != "FP")
+ out << pt1 << '\n' << pt2 << '\n';
+ }
+}
diff --git a/nmpfilter/nmplifilter.cpp b/nmpfilter/nmplifilter.cpp
new file mode 100644
index 00000000..6b16e500
--- /dev/null
+++ b/nmpfilter/nmplifilter.cpp
@@ -0,0 +1,23 @@
+// Travel Mapping Project, Eric Bryant, 2018
+#include
+#include
+#include
+using namespace std;
+
+int main(int argc, char *argv[])
+{ if (argc != 3)
+ { cout << "usage: nmplifilter InputFile OutputFile\n";
+ return 0;
+ }
+ ifstream in(argv[1]);
+ if (!in)
+ { std::cout << argv[1] << " not found\n";
+ return 0;
+ }
+ string pt1, pt2;
+ ofstream out(argv[2]);
+ while (getline(in, pt1) && getline(in, pt2))
+ { if (pt1.substr(pt1.size()-2, 2) == "LI" || pt2.substr(pt2.size()-2, 2) == "LI")
+ out << pt1 << '\n' << pt2 << '\n';
+ }
+}