Skip to content

v0.2.48..v0.2.49 changeset NodeDensityTilesCmd.cpp

Garret Voltz edited this page Oct 2, 2019 · 1 revision
diff --git a/hoot-rnd/src/main/cpp/hoot/rnd/cmd/NodeDensityTilesCmd.cpp b/hoot-rnd/src/main/cpp/hoot/rnd/cmd/NodeDensityTilesCmd.cpp
index 70a5f0f..94d62fd 100644
--- a/hoot-rnd/src/main/cpp/hoot/rnd/cmd/NodeDensityTilesCmd.cpp
+++ b/hoot-rnd/src/main/cpp/hoot/rnd/cmd/NodeDensityTilesCmd.cpp
@@ -27,25 +27,17 @@
 
 // Hoot
 #include <hoot/core/cmd/BaseCommand.h>
-#include <hoot/core/conflate/tile/TileBoundsCalculator.h>
 #include <hoot/core/elements/OsmMap.h>
 #include <hoot/core/io/ApiDbReader.h>
 #include <hoot/core/io/OsmMapReader.h>
 #include <hoot/core/io/OsmMapReaderFactory.h>
 #include <hoot/core/io/OsmMapWriterFactory.h>
 #include <hoot/core/util/Factory.h>
-#include <hoot/core/util/GeometryUtils.h>
+#include <hoot/rnd/util/TileUtils.h>
 #include <hoot/core/util/OpenCv.h>
 #include <hoot/core/util/Log.h>
-#include <hoot/core/util/RandomNumberUtils.h>
 #include <hoot/core/visitors/CalculateMapBoundsVisitor.h>
 
-// Qt
-#include <QTemporaryFile>
-
-// Tgs
-#include <tgs/Statistics/Random.h>
-
 namespace hoot
 {
 
@@ -89,11 +81,11 @@ public:
     LOG_VARD(inputs);
 
     const QString output = args[1];
-    if (!output.toLower().endsWith(".geojson"))
+    if (!output.toLower().endsWith(".geojson") && !output.toLower().endsWith(".osm"))
     {
-      throw HootException(
-        "Invalid output file format: " + output + ".  Only the GeoJSON output format is " +
-        "supported.");
+      throw IllegalArgumentException(
+        "Invalid output file format: " + output + ".  Only the GeoJSON (.geojson) and OSM " +
+        "(.osm) output formats are supported.");
     }
     LOG_VARD(output);
 
@@ -138,16 +130,26 @@ public:
     conf().set(ConfigOptions().getIdGeneratorKey(), "hoot::PositiveIdGenerator");
 
     OsmMapPtr inputMap = _readInputs(inputs);
+    long minNodeCountInOneTile = 0;
+    long maxNodeCountInOneTile = 0;
     const std::vector<std::vector<geos::geom::Envelope>> tiles =
-      _calculateTiles(maxNodesPerTile, pixelSize, inputMap);
-    _writeOutputAsGeoJson(tiles, output, args.contains("--random"), randomSeed);
+      TileUtils::calculateTiles(
+        maxNodesPerTile, pixelSize, inputMap, minNodeCountInOneTile, maxNodeCountInOneTile);
+    if (output.toLower().endsWith(".geojson"))
+    {
+      TileUtils::writeTilesToGeoJson(tiles, output, args.contains("--random"), randomSeed);
+    }
+    else
+    {
+      TileUtils::writeTilesToOsm(tiles, output, args.contains("--random"), randomSeed);
+    }
 
     return 0;
   }
 
 private:
 
-  OsmMapPtr _readInputs(const QStringList inputs)
+  OsmMapPtr _readInputs(const QStringList& inputs)
   {
     const bool bboxSpecified =
       !ConfigOptions().getConvertBoundingBox().trimmed().isEmpty() ||
@@ -157,8 +159,18 @@ private:
     OsmMapPtr map(new OsmMap());
     for (int i = 0; i < inputs.size(); i++)
     {
+      const QString input = inputs.at(i);
       std::shared_ptr<OsmMapReader> reader =
-        OsmMapReaderFactory::createReader(inputs.at(i), true, Status::Unknown1);
+        OsmMapReaderFactory::createReader(input, true, Status::Unknown1);
+
+      std::shared_ptr<Boundable> boundable = std::dynamic_pointer_cast<Boundable>(reader);
+      if (bboxSpecified && !boundable)
+      {
+        // TODO: need a test for this
+        throw IllegalArgumentException(
+          "Reader for " + input + " must implement Boundable when the " +
+          "convert.bounding.box configuration option is specified.");
+      }
 
       std::shared_ptr<ApiDbReader> apiDbReader =
         std::dynamic_pointer_cast<ApiDbReader>(reader);
@@ -170,15 +182,8 @@ private:
         //convert.bounding.box, if it was specified (ways partially inside the bounds, etc.)
         apiDbReader->setReturnNodesOnly(true);
       }
-      if (bboxSpecified && !apiDbReader)
-      {
-        //non api db readers don't support convert.bounding.box right now and are just going to
-        //generate confusing output with its specified, so let's throw
-        throw HootException(
-          "convert.bounding.box configuration option specified for a non API DB reader");
-      }
 
-      reader->open(inputs.at(i));
+      reader->open(input);
       reader->read(map);
     }
     LOG_VARD(map->getNodeCount());
@@ -190,148 +195,6 @@ private:
 
     return map;
   }
-
-  std::vector<std::vector<geos::geom::Envelope>> _calculateTiles(const long maxNodesPerTile,
-                                                                   const double pixelSize,
-                                                                   OsmMapPtr map)
-  {
-    TileBoundsCalculator tileBoundsCalculator(pixelSize);
-    tileBoundsCalculator.setMaxNodesPerBox(maxNodesPerTile);
-    //tbc.setSlop(0.1);
-    cv::Mat r1, r2;
-    tileBoundsCalculator.renderImage(map, r1, r2);
-    //we're calculating for unknown1 only, so fill the second matrix with all zeroes
-    cv::Mat zeros = cv::Mat::zeros(r1.size(), r1.type());
-    tileBoundsCalculator.setImages(r1, zeros);
-    return tileBoundsCalculator.calculateTiles();
-  }
-
-  void _writeOutputAsGeoJson(const std::vector<std::vector<geos::geom::Envelope>>& tiles,
-                             const QString& outputPath, const bool selectSingleRandomTile,
-                             int randomSeed)
-  {
-    //write out to temp osm and then use ogr2ogr to convert to geojson
-
-    QTemporaryFile osmTempFile("tmp/tiles-calculate-temp-XXXXXX.osm");
-    if (!osmTempFile.open())
-    {
-      throw HootException(
-        "Unable to open OSM temp file: " + osmTempFile.fileName() + " for GeoJSON output.");
-    }
-    LOG_VARD(osmTempFile.fileName());
-    _writeOutputAsOsm(tiles, osmTempFile.fileName(), selectSingleRandomTile, randomSeed);
-
-    QFile outFile(outputPath);
-    if (outFile.exists() && !outFile.remove())
-    {
-      throw HootException("Unable to open GeoJSON file: " + outputPath + " for writing.");
-    }
-    LOG_VARD(outputPath);
-
-    //exporting as multipolygons, as that's what the Tasking Manager expects
-    const QString cmd =
-      "ogr2ogr -f GeoJSON -select \"name,boundary,osm_way_id\" " + outputPath + " " +
-      osmTempFile.fileName() + " multipolygons";
-    LOG_VARD(cmd);
-    LOG_INFO("Writing output to " << outputPath);
-    const int retval = std::system(cmd.toStdString().c_str());
-    if (retval != 0)
-    {
-      throw HootException(
-        "Failed converting " + osmTempFile.fileName() + " to GeoJSON: " + outputPath +
-        ".  Status: " + QString::number(retval));
-    }
-  }
-
-  void _writeOutputAsOsm(const std::vector<std::vector<geos::geom::Envelope>>& tiles,
-                         const QString& outputPath, const bool selectSingleRandomTile,
-                         int randomSeed)
-  {
-    LOG_VARD(outputPath);
-
-    int randomTileIndex = -1;
-    if (selectSingleRandomTile)
-    {
-      if (randomSeed == -1)
-      {
-        randomSeed = RandomNumberUtils::generateSeed();
-      }
-      LOG_VARD(randomSeed);
-      Tgs::Random::instance()->seed(randomSeed);
-
-      const size_t numBboxes = tiles.size() * tiles[0].size();
-      randomTileIndex = Tgs::Random::instance()->generateInt(numBboxes);
-    }
-
-    OsmMapPtr boundaryMap(new OsmMap());
-    int bboxCtr = 1;
-    for (size_t tx = 0; tx < tiles.size(); tx++)
-    {
-      for (size_t ty = 0; ty < tiles[tx].size(); ty++)
-      {
-        // TODO: This code could possibly be replaced by GeometryUtils::createMapFromBounds.
-        const geos::geom::Envelope env = tiles[tx][ty];
-        const double circularError = ConfigOptions().getCircularErrorDefaultValue();
-
-        NodePtr lowerLeft(
-          new Node(
-            Status::Unknown1,
-            boundaryMap->createNextNodeId(),
-            geos::geom::Coordinate(env.getMinX(), env.getMinY()),
-            circularError));
-        boundaryMap->addNode(lowerLeft);
-        NodePtr upperRight(
-          new Node(
-            Status::Unknown1,
-            boundaryMap->createNextNodeId(),
-            geos::geom::Coordinate(env.getMaxX(), env.getMaxY()),
-            circularError));
-        boundaryMap->addNode(upperRight);
-        NodePtr upperLeft(
-          new Node(
-            Status::Unknown1,
-            boundaryMap->createNextNodeId(),
-            geos::geom::Coordinate(env.getMinX(), env.getMaxY()),
-            circularError));
-        boundaryMap->addNode(upperLeft);
-        NodePtr lowerRight(
-          new Node(
-            Status::Unknown1,
-            boundaryMap->createNextNodeId(),
-            geos::geom::Coordinate(env.getMaxX(), env.getMinY()),
-            circularError));
-        boundaryMap->addNode(lowerRight);
-
-        WayPtr bbox(new Way(Status::Unknown1, boundaryMap->createNextWayId(), circularError));
-        bbox->addNode(lowerLeft->getId());
-        bbox->addNode(upperLeft->getId());
-        bbox->addNode(upperRight->getId());
-        bbox->addNode(lowerRight->getId());
-        bbox->addNode(lowerLeft->getId());
-        //gdal will recognize any closed way with the boundary tag as a polygon (tags
-        //for features recognized as polys configurable in osmconf.ini), which is the type of
-        //output we want
-        bbox->setTag("boundary", "task_grid_cell");
-        bbox->setTag("name", "Task Grid Cell #" + QString::number(bboxCtr));
-        if (!selectSingleRandomTile ||
-            (selectSingleRandomTile && (bboxCtr - 1) == randomTileIndex))
-        {
-          boundaryMap->addWay(bbox);
-        }
-        bboxCtr++;
-      }
-    }
-
-    OsmMapWriterFactory::write(boundaryMap, outputPath);
-
-  //      if (Log::getInstance().getLevel() <= Log::Debug)
-  //      {
-  //        const QString debugOutputPath = "tmp/calc-tiles-osm-debug.osm";
-  //        QFile outFile(outputPath);
-  //        LOG_DEBUG("writing debug output to " << debugOutputPath);
-  //        outFile.copy(debugOutputPath);
-  //      }
-  }
 };
 
 HOOT_FACTORY_REGISTER(Command, NodeDensityTilesCmd)
Clone this wiki locally