v0.2.47..v0.2.48 changeset ChangesetApplyCmd.cpp
Garret Voltz edited this page Sep 27, 2019
·
1 revision
diff --git a/hoot-core/src/main/cpp/hoot/core/cmd/ChangesetApplyCmd.cpp b/hoot-core/src/main/cpp/hoot/core/cmd/ChangesetApplyCmd.cpp
new file mode 100644
index 0000000..e4e57ad
--- /dev/null
+++ b/hoot-core/src/main/cpp/hoot/core/cmd/ChangesetApplyCmd.cpp
@@ -0,0 +1,204 @@
+/*
+ * This file is part of Hootenanny.
+ *
+ * Hootenanny is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --------------------------------------------------------------------
+ *
+ * The following copyright notices are generated automatically. If you
+ * have a new notice to add, please use the format:
+ * " * @copyright Copyright ..."
+ * This will properly maintain the copyright information. DigitalGlobe
+ * copyrights will be updated automatically.
+ *
+ * @copyright Copyright (C) 2016, 2017, 2018, 2019 DigitalGlobe (http://www.digitalglobe.com/)
+ */
+
+// Hoot
+#include <hoot/core/cmd/BaseCommand.h>
+#include <hoot/core/io/MapStatsWriter.h>
+#include <hoot/core/io/OsmApiDbSqlChangesetApplier.h>
+#include <hoot/core/io/OsmApiWriter.h>
+#include <hoot/core/util/Factory.h>
+#include <hoot/core/util/FileUtils.h>
+
+// Qt
+#include <QFile>
+#include <QFileInfo>
+
+using namespace std;
+
+namespace hoot
+{
+
+class ChangesetApplyCmd : public BaseCommand
+{
+public:
+
+ static string className() { return "hoot::ChangesetApplyCmd"; }
+
+ ChangesetApplyCmd() { }
+
+ virtual QString getName() const override { return "changeset-apply"; }
+
+ virtual QString getDescription() const override { return "Writes an OSM changeset"; }
+
+ virtual int runSimple(QStringList& args) override
+ {
+ bool showStats = false;
+ bool showProgress = false;
+ // Check for stats flag
+ if (args.contains("--stats"))
+ {
+ showStats = true;
+ args.removeAll("--stats");
+ }
+ // Check for progress flag
+ if (args.contains("--progress"))
+ {
+ showProgress = true;
+ args.removeAll("--progress");
+ }
+
+ // Make sure that there are at least two other arguments
+ if (args.size() < 2)
+ {
+ cout << getHelp() << endl << endl;
+ throw HootException(
+ QString("%1 takes at least two parameters and was given %2 parameters")
+ .arg(getName())
+ .arg(args.size()));
+ }
+
+ Progress progress(ConfigOptions().getJobId(), "Apply Changeset", Progress::JobState::Running);
+
+ // Write changeset/OSM XML to OSM API
+ if (args[0].endsWith(".osc") || args[0].endsWith(".osm"))
+ {
+ // Get the endpoint URL
+ const QString urlStr = args[args.size() - 1];
+ if (!urlStr.toLower().startsWith("http://") || !urlStr.toLower().startsWith("https://"))
+ {
+ throw IllegalArgumentException(
+ QString("XML changesets must be written to an OpenStreetMap compatible web service. ") +
+ QString("Tried to write to: " + urlStr));
+ }
+ QUrl osm;
+ osm.setUrl(args[args.size() - 1]);
+
+ // Create a URL without user info for logging
+ QString printableUrl = osm.toString(QUrl::RemoveUserInfo);
+
+ const int maxFilePrintLength = ConfigOptions().getProgressVarPrintLengthMax();
+
+ // Grab all the changeset files
+ QList<QString> changesets;
+ for (int i = 0; i < args.size() - 1; ++i)
+ {
+ progress.set(
+ 0.0,
+ "Adding changeset: ..." + args[i].right(maxFilePrintLength) + " for application to: " +
+ printableUrl.left(maxFilePrintLength) + "...", true);
+ changesets.append(args[i]);
+ }
+
+ // Do the actual splitting and uploading
+ OsmApiWriter writer(osm, changesets);
+ writer.showProgress(showProgress);
+ if (showProgress)
+ {
+ // OsmApiWriter is doing all the progress reporting but will pass a progress object in
+ // anyway, just in case we ever add any extra job steps outside of it.
+ writer.setProgress(progress);
+ }
+ writer.apply();
+
+ progress.set(
+ 1.0, writer.containsFailed() ? Progress::JobState::Failed : Progress::JobState::Successful,
+ "Changeset(s) applied to: " + printableUrl.left(maxFilePrintLength) + "...");
+
+ // Write out the failed changeset if there is one
+ if (writer.containsFailed())
+ {
+ // Output the errors from 'changeset.osc' to 'changeset-error.osc'
+ QFileInfo path(args[0]);
+ QString errorFilename =
+ path.absolutePath() + QDir::separator() +
+ path.baseName() + "-error." + path.completeSuffix();
+ LOG_ERROR(
+ QString("Some changeset elements failed to upload. Stored in %1.").arg(errorFilename));
+ FileUtils::writeFully(errorFilename, writer.getFailedChangeset());
+ }
+
+ // Output the stats if requested
+ if (showStats)
+ {
+ // Jump through a few hoops to use the MapStatsWriter
+ QList<QList<SingleStat>> allStats;
+ allStats.append(writer.getStats());
+ QString statsMsg = MapStatsWriter().statsToString(allStats, "\t");
+ cout << "stats = (stat)\n" << statsMsg << endl;
+ }
+ }
+ // Write changeset SQL directly to the database
+ else if (args[0].endsWith(".osc.sql"))
+ {
+ // Not worrying about progress updates for SQL changesets, since those will eventually go
+ // away (#3156).
+
+ if (args.size() != 2 && args.size() != 4)
+ {
+ cout << getHelp() << endl << endl;
+ throw HootException(
+ QString("%1 takes two or four parameters and was given %2 parameters")
+ .arg(getName())
+ .arg(args.size()));
+ }
+
+ LOG_INFO("Applying changeset " << args[0] << " to " << args[1] << "...");
+
+ QUrl url(args[1]);
+ OsmApiDbSqlChangesetApplier changesetWriter(url);
+
+ if (args.size() == 4)
+ {
+ if (changesetWriter.conflictExistsInTarget(args[2], args[3]))
+ {
+ //Don't like throwing an exception here from the command line, but this error needs to
+ //bubble up to the web service.
+ //The better thing to do here would be to return an error code and have the services
+ //scripts look for it, I think.
+ throw HootException(
+ "The changeset will not be written because conflicts exist in the target OSM API database.");
+ }
+ }
+
+ QFile changesetSqlFile(args[0]);
+ changesetWriter.write(changesetSqlFile);
+ //The tests rely on this being output, so leave it as a cout and not a log statement.
+ cout << changesetWriter.getChangesetStats();
+ }
+ else
+ {
+ throw HootException("Invalid changeset file format: " + args[0]);
+ }
+
+ return 0;
+ }
+};
+
+HOOT_FACTORY_REGISTER(Command, ChangesetApplyCmd)
+
+}
+