Example #1
0
        public static void UnlinkGNIS()
        {
            var osm = OpenFile(@"C:\Users\Alex\Downloads\Maine\maine-latest-internal.osm.pbf").ToArray();

            var badsC = osm.Where(e => e.Tags != null && e.Tags.Contains("gnis:reviewed", "no") && e.UserName == "blackboxlogic+bot").ToDictionary(b => b.Id);

            var bads    = osm.Where(e => e.Tags != null && e.Tags.Contains("gnis:reviewed", "no") && e.UserName == "blackboxlogic+bot" && e is Node).ToDictionary(b => b.Id);
            var prevs   = OsmApiClient.GetElements(bads.Values.ToDictionary(b => new OsmGeoKey(b), b => b.Version - 1)).Result;
            var news    = new List <OsmGeo>();
            var changes = new List <OsmGeo>();

            foreach (var prev in prevs.Where(p => !p.Tags.ContainsKey("addr:street") &&
                                             p.Id != 367794548 && p.Id != 367795033 && p.Id != 367794682 && p.Id != 367794721 && p.Id != 367795011 &&
                                             p.Id != 367795009 && p.Id != 367794614 && p.Id != 367794612))
            {
                prev.Version++;
                changes.Add(prev);

                var bad = bads[prev.Id];
                bad.Tags = new TagsCollection(bad.Tags.Where(t => t.Key.StartsWith("addr")));
                bad.Id   = -bad.Id;
                news.Add(bad);
            }

            var change = Changes.FromGeos(news, changes, null);

            FileSerializer.WriteXml("FIX unmerge gnis\\change.osc", change);
            var changeTags = GetCommitTags("Separate addreses which should not have been merged into GNIS elements with reviewed=no");
            var ids        = Subjects.UploadChange(change, changeTags, "FIX unmerge gnis").Result;

            //Console.WriteLine(string.Join(Environment.NewLine, bads.Values.Select(b => b.Type + " " + b.Id)));
        }
Example #2
0
        public static void CountNoName()
        {
            var osm = OpenFile(@"C:\Users\Alex\Downloads\Maine\maine-latest-internal.osm.pbf").ToArray();

            var nodes = osm.OfType <Node>().ToDictionary(n => n.Id, n => n.AsPosition());

            var junctions = new HashSet <long>(osm.OfType <Way>().SelectMany(w => w.Nodes).GroupBy(n => n).Where(g => g.Count() > 1).Select(g => g.Key));

            var noName = osm.Where(e => e is Way && e.Tags != null && e.Tags.ContainsKey("highway") && !e.Tags.ContainsKey("name") && !e.Tags.ContainsKey("ref"))
                         .GroupBy(e => e.Tags["highway"])
                         .ToDictionary();

            var residential = noName["residential"].OfType <Way>().Where(r => r.Nodes.Count(n => junctions.Contains(n)) == 1 &&
                                                                         Geometry.DistanceMeters(nodes[r.Nodes.First()], nodes[r.Nodes.Last()]) < 100).ToArray();

            var residentialNodes = new HashSet <long>(residential.SelectMany(r => r.Nodes));

            var elements = residential.OfType <OsmGeo>().Concat(osm.OfType <Node>().Where(n => residentialNodes.Contains(n.Id.Value)));

            FileSerializer.WriteXml("FIXCountNoName\\FIXCountNoName.osm", elements.AsOsm());

            Console.WriteLine(residential.Length);

            //Console.WriteLine(SummarizeGrouping(noName));
        }
Example #3
0
        public static void FixPlaces()
        {
            var osm = OpenFile(@"C:\Users\Alex\Downloads\maine-latest-internal.osm.pbf").ToArray();

            var index = OsmSharp.Db.Impl.Extensions.CreateSnapshotDb(new MemorySnapshotDb(osm));

            var roadsByName = osm.Where(e => e.Tags != null && e.Tags.ContainsKey("highway") && e.Tags.ContainsKey("name"))
                              .GroupBy(e => Connonical(e.Tags["name"])).ToDictionary();
            var placesByName = osm.Where(e => e.Tags != null && e.Tags.ContainsKey("name") && (e.Tags.ContainsKey("place") || e.Tags.ContainsKey("waterway")))
                               .GroupBy(e => Connonical(e.Tags["name"])).ToDictionary();
            var orphans = osm.Where(e => e.Tags != null && e.Tags.Contains("addr:state", "ME") && e.Tags.ContainsKey("addr:street") && !roadsByName.ContainsKey(Connonical(e.Tags["addr:street"])) &&
                                    placesByName.ContainsKey(Connonical(e.Tags["addr:street"])) &&
                                    placesByName[Connonical(e.Tags["addr:street"])].Any(
                                        p => Geometry.MinDistanceMeters(e.AsComplete(index).AsPosition(), p.AsComplete(index)) < 1000)).ToArray();

            var sad = osm.Where(e => e.Tags != null && e.Tags.ContainsKey("addr:street") && e.Tags["addr:street"].Contains("Island") && !roadsByName.ContainsKey(Connonical(e.Tags["addr:street"])) &&
                                (!placesByName.ContainsKey(Connonical(e.Tags["addr:street"])) ||
                                 !placesByName[Connonical(e.Tags["addr:street"])].Any(
                                     p => Geometry.MinDistanceMeters(e.AsComplete(index).AsPosition(), p.AsComplete(index)) < 1000))).ToArray();

            FileSerializer.WriteXml(@"Fix IslandPlaces\islands.osm", placesByName.Values.SelectMany(ps => ps).WithChildren(index).AsOsm());
            FileSerializer.WriteXml(@"Fix IslandPlaces\orphans.osm", orphans.WithChildren(index).AsOsm());
            FileSerializer.WriteXml(@"Fix IslandPlaces\sadOrphans.osm", sad.WithChildren(index).AsOsm());

            foreach (var orphan in orphans)
            {
                orphan.Tags["addr:place"] = orphan.Tags["addr:street"];
                orphan.Tags.RemoveKey("addr:street");
            }

            var change     = Changes.FromGeos(null, orphans, null);
            var changeTags = GetCommitTags("Moving addr:street tag to addr:place for island addresses.");
            var ids        = Subjects.UploadChange(change, changeTags, "Fix IslandPlaces").Result;
        }
Example #4
0
        public static void FixIslandPlaces()
        {
            var osm   = OpenFile(@"C:\Users\Alex\Downloads\Maine\maine-latest-internal.osm.pbf").ToArray();
            var index = OsmSharp.Db.Impl.Extensions.CreateSnapshotDb(new MemorySnapshotDb(osm));

            var roadNames     = new HashSet <string>(osm.Where(e => e.Tags != null && e.Tags.ContainsKey("highway") && e.Tags.ContainsKey("name")).Select(e => Connonical(e.Tags["name"])).Distinct());
            var places        = osm.Where(e => e.Tags != null && (e.Tags.ContainsKey("place") || e.Tags.ContainsKey("waterway")));
            var islands       = places.Where(e => e.Tags["place"] == "island" || e.Tags["place"] == "islet" || e.Tags.Any(t => t.Key == "name" && t.Value.Contains("island", StringComparison.OrdinalIgnoreCase))).ToArray();
            var islandsByName = islands.Where(e => e.Tags.ContainsKey("name")).GroupBy(e => Connonical(e.Tags["name"])).ToDictionary();

            var orphans = osm.Where(e => e.Tags != null && e.Tags.ContainsKey("addr:street") && !roadNames.Contains(Connonical(e.Tags["addr:street"])) &&
                                    islandsByName.ContainsKey(Connonical(e.Tags["addr:street"]))).ToArray();

            var matchedOrphans = orphans.Where(e => islandsByName[Connonical(e.Tags["addr:street"])].Any(i => Geometry.MinDistanceMeters(e.AsComplete(index).AsPosition(), i.AsComplete(index)) < 1000)).ToArray();

            FileSerializer.WriteXml(@"Fix IslandPlaces\islands.osm", islands.WithChildren(index).AsOsm());
            FileSerializer.WriteXml(@"Fix IslandPlaces\orphans.osm", orphans.WithChildren(index).AsOsm());
            FileSerializer.WriteXml(@"Fix IslandPlaces\matchedOrphans.osm", matchedOrphans.WithChildren(index).AsOsm());
            FileSerializer.WriteXml(@"Fix IslandPlaces\unmatchedOrphans.osm", orphans.Except(matchedOrphans).WithChildren(index).AsOsm());

            foreach (var orphan in orphans)
            {
                orphan.Tags["addr:place"] = orphan.Tags["addr:street"];
                orphan.Tags.RemoveKey("addr:street");
            }

            var change     = Changes.FromGeos(null, orphans, null);
            var changeTags = GetCommitTags("Moving addr:street tag to addr:place for island addresses.");
            var ids        = Subjects.UploadChange(change, changeTags, "Fix IslandPlaces").Result;
        }
Example #5
0
        public static void SummarizeKeys()
        {
            var osm = OpenFile(@"C:\Users\Alex\Downloads\maine-latest-internal.osm.pbf")
                      .Where(e => e.UserId == 10307443).ToArray();

            FileSerializer.WriteXml("blackboxlogic.osm", osm.AsOsm());
        }
Example #6
0
        public static void FIXDupeNames()
        {
            var osm   = OpenFile(@"C:\Users\Alex\Downloads\maine-latest-internal.osm.pbf");
            var index = OsmSharp.Db.Impl.Extensions.CreateSnapshotDb(new MemorySnapshotDb(osm));

            var named = osm.Where(e => (e is Node || e is Way) && e.Tags != null && e.Tags.ContainsKey("name") && !e.Tags.ContainsKey("highway") && !e.Tags.ContainsKey("railway") && !e.Tags.ContainsKey("waterway"))
                        .GroupBy(e => Connonical(e.Tags["name"]))
                        .Where(g => g.Count() > 1)
                        .ToDictionary();

            var dupes       = named.ToDictionary(kvp => kvp.Key, kvp => Geometry.GroupCloseNeighbors(kvp.Value, 1000, index, false).Where(c => c.Count > 1).OrderByDescending(c => c.Count).ToArray()).OrderByDescending(d => d.Value.Length).ToArray();
            var dupeCount   = dupes.SelectMany(d => d.Value.SelectMany(r => r)).Count();
            var av          = dupes.SelectMany(d => d.Value).Average(d => d.Count);
            var ma          = dupes.SelectMany(d => d.Value).Max(d => d.Count);
            var myDupes     = dupes.ToDictionary(d => d.Key, d => d.Value.Where(c => c.Any(e => e.UserName.StartsWith("blackbo"))).ToArray()).Where(c => c.Value.Any()).OrderByDescending(c => c.Value.Count()).ToArray();
            var myDupeCount = myDupes.SelectMany(d => d.Value.SelectMany(r => r)).Count();

            var myOsm = myDupes.SelectMany(d => d.Value.SelectMany(r => r)).ToArray().WithChildren(index).AsOsm();

            FileSerializer.WriteXml("FIXDupeNames\\myDupes.osm", myOsm);

            var allOsm = dupes.SelectMany(d => d.Value.SelectMany(r => r)).ToArray().WithChildren(index).AsOsm();

            FileSerializer.WriteXml("FIXDupeNames\\allDupes.osm", allOsm);
        }
Example #7
0
        private static NetTopologySuite.Geometries.Geometry LoadBounds(string scopeName, long relationId)
        {
            var scopeFile = $"{scopeName}\\Scope.osm";
            var osm       = FileSerializer.ReadXml <Osm>(scopeFile);
            CompleteRelation relation;

            if (osm != null)
            {
                relation = osm.GetElements().ToComplete().OfType <CompleteRelation>().First();
            }
            else
            {
                NonAuthClient client = new NonAuthClient("https://www.osm.org/api/", new HttpClient());
                relation = client.GetCompleteRelation(relationId).Result;
                FileSerializer.WriteXml(scopeFile, relation.ToSimpleWithChildren().AsOsm());
            }

            var geometry = FeatureInterpreter.DefaultInterpreter.Interpret(relation).First().Geometry;

            if (geometry is LinearRing linRing)
            {
                var polygon = Polygon.DefaultFactory.CreatePolygon(linRing);                 // Or multipolygon?
                return(polygon);
            }
            else if (geometry is MultiPolygon multi)
            {
                return(multi);
                //Polygon.DefaultFactory.CreateMultiPolygon();
            }

            throw new Exception("unexpected geometry type: " + geometry.GetType().Name);
        }
Example #8
0
        public static void CombineSegments()
        {
            var osm      = OpenFile(@"C:\Users\Alex\Downloads\Franklin_Full_Street_Name_simplified.osm").ToArray();
            var index    = OsmSharp.Db.Impl.Extensions.CreateSnapshotDb(new MemorySnapshotDb(osm));
            var highways = osm.OfType <Way>()
                           .Where(w => w.Tags != null && w.Tags.ContainsKey("highway") && w.Tags.ContainsKey("name")).ToArray();
            var highwaysWithChildren = Geometry.CombineSegments(highways).WithChildren(index);

            FileSerializer.WriteXml("Touchers.osm", highwaysWithChildren.AsOsm());
        }
        private void DoConflate()
        {
            var reference = GetReference();
            var subject   = GetSubject(reference.Bounds.ExpandBy(15));
            var Change    = Conflate.Merge(reference, subject, Municipality, Static.Municipalities[Municipality]);

            FileSerializer.WriteXml(Municipality + "/Conflated.osc", Change);
            References.Report(reference.GetElements().ToArray());
            Conflate.Review(Municipality);
        }
Example #10
0
        private static void Go(string scopeName, long relationId)
        {
            if (!Directory.Exists(scopeName))
            {
                Directory.CreateDirectory(scopeName);
            }
            //NetTopologySuite.Algorithm.Match.HausdorffSimilarityMeasure
            //Geometry.DistanceMeters(
            //	new NetTopologySuite.Geometries.Coordinate(scope.MinLongitude.Value, scope.MinLatitude.Value),
            //	new NetTopologySuite.Geometries.Coordinate(scope.MaxLongitude.Value, scope.MaxLatitude.Value));

            var scope     = scopeName == null ? null : LoadBounds(scopeName, relationId);
            var reference = Reference.Generate("ReferenceRawSimplified.osm", $"{scopeName}\\Reference.osm", scope);
            Osm subject;

            using (var stream = File.OpenRead("Subject.pbf"))
            {
                subject = new PBFOsmStreamSource(stream).AsOsm();
            }

            var missingPublic = Conflate.FindMissingRoads(subject, reference.Where(w => !w.Tags.Contains("ownership", "private")).ToArray());

            FileSerializer.WriteXml($"{scopeName}\\MissingPublic.osm", missingPublic);
            JOSM.MarkAsNeverUpload($"{scopeName}\\MissingPublic.osm");

            var missingPrivate = Conflate.FindMissingRoads(subject, reference.Where(w => w.Tags.Contains("ownership", "private")).ToArray());

            FileSerializer.WriteXml($"{scopeName}\\MissingPrivate.osm", missingPrivate);
            JOSM.MarkAsNeverUpload($"{scopeName}\\MissingPrivate.osm");

            //int i = 0;
            //var referenceSlices = BoundsExtentions.SliceRecusive(reference, 200);

            //foreach (var referenceSlice in referenceSlices)
            //{
            //	var directory = i.ToString("0000");
            //	Directory.CreateDirectory(directory);
            //	FileSerializer.WriteXml($"{directory}\\Reference.osm", referenceSlice.Value.AsOsm());
            //	JOSM.MarkAsNeverUpload($"{directory}\\Reference.osm");

            //	//var subjectSlice = subject.FilterBox(bounds.MinLongitude.Value, bounds.MaxLatitude.Value,
            //	//		bounds.MaxLongitude.Value, bounds.MinLatitude.Value, true)
            //	//	.AsOsm();
            //	//FileSerializer.WriteXml($"{i}\\Subject.osm", subjectSlice);
            //	//var missing = Conflate.FindMissingPublicRoads(subjectSlice, referenceSlice);
            //	//FileSerializer.WriteXml($"{i}\\MissingPublicRoads.osm", missing);
            //	//var session = File.ReadAllText("Session.jos");
            //	// Transform session
            //	//File.WriteAllText($"{i}\\Session.jos", session);
            //	i++;
            //}
        }
Example #11
0
        public static void GetSegments()
        {
            var osm      = OpenFile(@"C:\Users\Alex\Downloads\Franklin_Full_Street_Name_simplified.osm").ToArray();
            var index    = OsmSharp.Db.Impl.Extensions.CreateSnapshotDb(new MemorySnapshotDb(osm));
            var highways = osm.OfType <Way>()
                           .Where(w => w.Tags != null && w.Tags.ContainsKey("highway") && w.Tags.ContainsKey("name")).ToList();
            var touchers = highways.GroupBy(w => w.Tags["name"])
                           .SelectMany(g => g.Where(w => g.Any(other => w != other && EndsAreTouching(w, other))))
                           .WithChildren(index)
                           .AsOsm();

            FileSerializer.WriteXml("Touchers.osm", touchers);
        }
Example #12
0
        public static void FixImportedNames()
        {
            var nameIndex = OneOffs.GetAllChangeElements(e => e.Tags != null && e.Tags.ContainsKey("addr:unit"))
                            .GroupBy(e => e.Tags["addr:unit"])
                            .ToDictionary(g => g.Key, g => g.ToArray());

            Console.Write(string.Join(Environment.NewLine,
                                      nameIndex.OrderByDescending(kvp => kvp.Value.Length).Select(kvp => kvp.Value.Length + "x: " + kvp.Key + "\texample: " + kvp.Value.First().Type + " " + kvp.Value.First().Id)));
            var nameDestinations = new Dictionary <string, string>
            {
            };
            var toUpdate = new List <OsmGeo>();

            var currentKeys = nameDestinations.Keys.SelectMany(n => nameIndex[n].Select(e => new OsmGeoKey(e))).Distinct().ToArray();
            var allCurrents = GetCurrentVersionOfElements(currentKeys).ToDictionary(e => new OsmGeoKey(e));
            var missing     = currentKeys.Except(allCurrents.Keys).Select(k => k.Type + " " + k.Id).ToArray();

            foreach (var name in nameDestinations)
            {
                var keys = nameIndex[name.Key].Select(e => new OsmGeoKey(e)).ToArray();

                var currents = keys
                               .Where(k => allCurrents.ContainsKey(k))
                               .Select(k => allCurrents[k])
                               .Where(e => e.Visible == true && e.Tags != null && e.Tags.Contains("addr:unit", name.Key))
                               .ToArray();

                foreach (var element in currents)
                {
                    if (name.Value == null)
                    {
                        element.Tags.RemoveKey("addr:unit");
                    }
                    else
                    {
                        element.Tags.AddOrReplace("addr:unit", name.Value);
                    }

                    toUpdate.Add(element);
                }
            }

            var fix = new OsmChange()
            {
                Modify = toUpdate.ToArray()
            };

            FileSerializer.WriteXml("temp change.osc", fix);
            var changesetTags = GetCommitTags($"Fixing bad addr:unit previously imported.");
            var fixIds        = Subjects.UploadChange(fix, changesetTags, @"FIX Offices\Uploaded\").Result;
        }
Example #13
0
        private static async Task <long> UploadChangePart(this OsmChange part,
                                                          TagsCollection tags, BasicAuthClient client, string pathForFiles)
        {
            var changeSetId = await client.CreateChangeset(tags);

            Log.LogDebug($"Creating ChangeSet {changeSetId}");
            FileSerializer.WriteXml(Path.Combine(pathForFiles, $"{changeSetId}-Conflated.osc"), part);
            var diffResult = await client.UploadChangeset(changeSetId, part);

            FileSerializer.WriteXml(Path.Combine(pathForFiles, $"{changeSetId}-DiffResult.diff"), diffResult);
            await client.CloseChangeset(changeSetId);

            Log.LogDebug($"Closing ChangeSet {changeSetId}");
            return(changeSetId);
        }
Example #14
0
        public static void RecreateMissingDiffResultFiles()
        {
            Static.Municipalities = FileSerializer.ReadJson <Dictionary <string, GeoJsonAPISource.Municipality> >("MaineMunicipalities.json");
            var osmApiClient = new NonAuthClient(Static.Config["OsmApiUrl"], Static.HttpClient,
                                                 Static.LogFactory.CreateLogger <NonAuthClient>());

            foreach (var municipality in Static.Municipalities.Values.Where(m => m.ChangeSetIds.Any(id => id != -1)))
            {
                foreach (var changeId in municipality.ChangeSetIds)
                {
                    var diffPath = $"{municipality.Name}/Uploaded/{changeId}-DiffResult.diff";

                    if (!File.Exists(diffPath))
                    {
                        Console.WriteLine(diffPath);

                        var changePath = $"{municipality.Name}/Uploaded/{changeId}-Conflated.osc";
                        var change     = FileSerializer.ReadXml <OsmChange>(changePath);
                        var mine       = change.Create.OfType <Node>().ToArray();
                        var theirs     = osmApiClient.GetChangesetDownload(changeId).Result;
                        List <OsmGeoResult> results = theirs.Modify.Select(e => e.AsDiffResult()).ToList();
                        var map = Geometry.NodesInOrNearCompleteElements(theirs.Create.OfType <Node>().ToArray(), mine, 0, 0, new HashSet <long>());
                        if (map.Count != theirs.Create.Length || map.Any(pair => pair.Value.Count(v => Tags.AreEqual(v.Tags, pair.Key.Tags)) != 1))
                        {
                            throw new Exception("bad map");
                        }
                        results.AddRange(map.Select(pair => pair.Value.Single(v => Tags.AreEqual(v.Tags, pair.Key.Tags)).AsDiffResult(pair.Key.Id, 1)));
                        var diffResult = new DiffResult()
                        {
                            Version = 0.6, Generator = "OsmPipeline", Results = results.ToArray()
                        };
                        FileSerializer.WriteXml(diffPath, diffResult);
                    }
                }
            }
        }
Example #15
0
        // nohousenumber=yes
        // consider just leaving out all addr:unit=*
        // option to moveNode or not. (Do you trust E911 locations more than OSM?)
        // MatchWidth list to handle multi-match

        // Maybe fix addr:street punctuation on elements that I didn't add or update
        // Maybe fix addr:street should be addr:place on elements that I didn't add or update
        // Maybe fix nodes merging into buildings on elements that I didn't add or update
        public void Main()
        {
            Static.Municipalities = FileSerializer.ReadJsonCacheOrSource("MaineMunicipalities.json",
                                                                         GetMunicipalities).Result;
            ShowProgress();
            Municipality = Static.Municipalities.Values.First(m => !m.ChangeSetIds.Any()).Name;
            Console.WriteLine("Starting in " + Municipality);

            while (true)
            {
                Console.Write("> ");
                var userInput = Console.ReadLine();

                if (Is(userInput, "reference"))
                {
                    //Func<Feature, bool> filter = f => (f.Geometry as Point).Coordinates.Longitude >= -70.505;
                    var reference = References.Fetch(Municipality).Result;
                    FileSerializer.WriteXml(Municipality + "/Reference.osm", reference);
                    File.Delete(Municipality + "/Conflated.osc");
                }
                else if (Is(userInput, "review ref"))
                {
                    var reference = GetReference();
                    References.Report(reference.GetElements().ToArray());
                }
                else if (Is(userInput, "subject"))
                {
                    var reference = GetReference();
                    var subject   = Subjects.GetElementsInBoundingBox(reference.Bounds.ExpandBy(15));
                    FileSerializer.WriteXml(Municipality + "/Subject.osm", subject);
                    File.Delete(Municipality + "/Conflated.osc");
                    Console.WriteLine("ChangeId high watermark: " + subject.GetHighestChangeSetId());
                }
                else if (Is(userInput, "conflate"))
                {
                    DoConflate();
                }
                else if (Is(userInput, "review con"))
                {
                    Conflate.Review(Municipality);
                }
                else if (Is(userInput, "review"))
                {
                    OpenJosm();
                }
                else if (Is(userInput, "list"))
                {
                    var key       = userInput.Split(" ")[1];
                    var reference = GetReference();
                    var values    = reference.GetElements().Where(e => e.Tags.ContainsKey(key)).Select(e => "\n\t" + e.Tags[key]).GroupBy(n => n).ToArray();

                    Console.WriteLine(string.Concat(values.Select(v => v.Key + "\tx" + v.Count())));
                }
                else if (Is(userInput, "filter"))
                {
                    var key       = userInput.Split(" ")[1];
                    var reference = GetReference();
                    DoFilter(key, reference.GetElements());
                    File.Delete(Municipality + "/Reference.osm");
                    File.Delete(Municipality + "/Conflated.osc");
                }
                else if (Is(userInput, "note"))
                {
                    Static.Municipalities[Municipality].Notes += "/n" + userInput.Split(' ', 2)[1];
                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                }
                else if (Is(userInput, "WhiteAll"))
                {
                    var review    = FileSerializer.ReadXml <Osm>(Municipality + "/Conflated.Review.osm");
                    var selection = review.GetElements()
                                    .Where(e => e.Tags != null && e.Tags.ContainsKey(Static.maineE911id))
                                    .Select(e => e.Tags[Static.maineE911id])
                                    .SelectMany(id => id.Split(new char[] { ' ', ',', ';', '-' }, StringSplitOptions.RemoveEmptyEntries))
                                    .Select(long.Parse)
                                    .Except(Static.Municipalities[Municipality].WhiteList)
                                    .ToArray();
                    Static.Municipalities[Municipality].WhiteList.AddRange(selection);

                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                    File.Delete(Municipality + "/Conflated.osc");
                }
                else if (Is(userInput, "white"))
                {
                    var selection = userInput.Split(' ', 2)[1]
                                    .Split(new char[] { ' ', ',', ';', '-', '=' }, StringSplitOptions.RemoveEmptyEntries)
                                    .Where(c => long.TryParse(c, out _))
                                    .Select(long.Parse)
                                    .Except(Static.Municipalities[Municipality].WhiteList)
                                    .ToArray();
                    Static.Municipalities[Municipality].WhiteList.AddRange(selection);

                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                    File.Delete(Municipality + "/Conflated.osc");
                }
                else if (Is(userInput, "blacktag"))
                {
                    var tag = userInput.Split(' ', 2)[1].Replace("maineE911id=", "");
                    Static.Municipalities[Municipality].BlackTags.Add(tag);
                    Static.Municipalities[Municipality].BlackTags = Static.Municipalities[Municipality].BlackTags.Distinct().ToList();
                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                    File.Delete(Municipality + "/Reference.osm");
                    File.Delete(Municipality + "/Conflated.osc");
                }
                else if (Is(userInput, "black"))
                {
                    AddToList(userInput.Split(' ', 2)[1], Static.Municipalities[Municipality].BlackList);
                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                    File.Delete(Municipality + "/Reference.osm");
                    File.Delete(Municipality + "/Conflated.osc");
                }
                else if (Is(userInput, "ignore"))
                {
                    AddToList(userInput.Split(' ', 2)[1], Static.Municipalities[Municipality].IgnoreList);
                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                }
                else if (Is(userInput, "commit"))
                {
                    var change  = FileSerializer.ReadXml <OsmChange>(Municipality + "/Conflated.osc");
                    var results = Subjects.UploadChange(change, Municipality).Result;
                    Static.Municipalities[Municipality].ChangeSetIds.AddRange(results);
                    Static.Municipalities[Municipality].ImportDate = DateTime.UtcNow;
                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                    Console.WriteLine("Finished!");

                    Next();
                }
                else if (Is(userInput, "skip"))
                {
                    Static.Municipalities[Municipality].Notes += " SKIPPED";
                    Static.Municipalities[Municipality].ChangeSetIds.Add(-1);
                    FileSerializer.WriteJson("MaineMunicipalities.json", Static.Municipalities);
                }
                else if (Is(userInput, "next"))
                {
                    Next();
                }
                else if (Is(userInput, "switch"))
                {
                    Municipality = ChooseMunicipality();
                    ShowProgress();
                }
                else if (Is(userInput, "folder"))
                {
                    OpenExplorer();
                }
                else if (Is(userInput, "remind"))
                {
                    var parts     = userInput.Split(' ', 3);
                    var id        = long.Parse(parts[1].Replace("maineE911id=", ""));
                    var reference = GetReference();
                    var element   = reference.Nodes.First(e => Math.Abs(e.Id.Value) == Math.Abs(id));
                    var message   = parts.Length > 2 ? parts[2] : "The addresses imported on this neighborhood need to be aligned with the correct buildings";
                    Subjects.CreateNote(element.Latitude.Value, element.Longitude.Value, message).Wait();
                }
                else if (Is(userInput, "help"))
                {
                    Console.WriteLine("Options:");
                    Console.WriteLine("\tSwitch");
                    Console.WriteLine("\tNext");
                    Console.WriteLine("\tReference");
                    Console.WriteLine("\tReview Reference");
                    Console.WriteLine("\tSubject");
                    Console.WriteLine("\tConflate");
                    Console.WriteLine("\tReview Conflate");
                    Console.WriteLine("\tReview");
                    Console.WriteLine("\tList [key]");
                    Console.WriteLine("\tFilter [key]");
                    Console.WriteLine("\t\t[Y/N/AUnit/Descr/Build/Move]");
                    Console.WriteLine("\tNote [message]");
                    Console.WriteLine("\tWhitelist [###]<,[###]...>");
                    Console.WriteLine("\tWhiteAll");
                    Console.WriteLine("\tBlacklist [###]<,[###]...>");
                    Console.WriteLine("\tBlackTag [###].[key] || *.[key]=[value]");
                    Console.WriteLine("\tIgnore [###]<,[###]...>");
                    Console.WriteLine("\tRemind [ref###] <message>");
                    Console.WriteLine("\tCommit");
                }
                else
                {
                    Console.WriteLine("What?");
                }

                Console.WriteLine("Done");
            }
        }