Skip to content

File test_gpkg.cpp

File List > io > tests > test_gpkg.cpp

Go to the documentation of this file

#include <gtest/gtest.h>

#include <fstream>
#include <set>
#include <vector>

#include "contour/contour.hpp"
#include "io/gpkg.hpp"
#include "polyline/polyline.hpp"
#include "testing/output_dir.hpp"
#include "utilities/coordinate.hpp"
#include "utilities/filesystem.hpp"
#include "utilities/progress_tracker.hpp"

// Test GPKGWriter basic functionality
TEST(GPKG, WriteAndReadContours) {
  // Create a temporary file path
  fs::path test_file = blaze::test::unique_test_output_path("contours", ".gpkg");

  // Clean up if file exists
  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }

  // Create a simple projection string (WGS84)
  std::string projection =
      R"(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]])";

  // Write contours
  {
    GPKGWriter writer(test_file.string(), projection);

    // Create test contours
    std::vector<Coordinate2D<double>> points1 = {
        Coordinate2D<double>(0.0, 0.0), Coordinate2D<double>(1.0, 0.0),
        Coordinate2D<double>(1.0, 1.0), Coordinate2D<double>(0.0, 1.0)};

    std::vector<Coordinate2D<double>> points2 = {Coordinate2D<double>(2.0, 2.0),
                                                 Coordinate2D<double>(3.0, 2.0),
                                                 Coordinate2D<double>(3.0, 3.0)};

    Polyline polyline1;
    polyline1.layer = "test_layer";
    polyline1.name = "contour_1";
    polyline1.vertices = points1;

    Polyline polyline2;
    polyline2.layer = "test_layer";
    polyline2.name = "contour_2";
    polyline2.vertices = points2;

    writer.write_polyline(polyline1, {{"elevation", 10.0}});
    writer.write_polyline(polyline2, {{"elevation", 20.0}});
  }

  // Read contours back
  std::vector<Contour> contours = read_gpkg(test_file, ProgressTracker());

  // Verify we got the contours back
  EXPECT_EQ(contours.size(), 2);

  // Check first contour
  EXPECT_DOUBLE_EQ(contours[0].height(), 10.0);
  EXPECT_EQ(contours[0].points().size(), 4);

  // Check second contour
  EXPECT_DOUBLE_EQ(contours[1].height(), 20.0);
  EXPECT_EQ(contours[1].points().size(), 3);

  // Clean up
  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }
}

// Test GPKGWriter with multiple layers
TEST(GPKG, MultipleLayers) {
  fs::path test_file = blaze::test::unique_test_output_path("multilayer", ".gpkg");

  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }

  std::string projection =
      R"(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]])";

  {
    GPKGWriter writer(test_file.string(), projection);

    Polyline polyline1;
    polyline1.layer = "layer1";
    polyline1.name = "line1";
    polyline1.vertices = {Coordinate2D<double>(0.0, 0.0), Coordinate2D<double>(1.0, 1.0)};

    Polyline polyline2;
    polyline2.layer = "layer2";
    polyline2.name = "line2";
    polyline2.vertices = {Coordinate2D<double>(2.0, 2.0), Coordinate2D<double>(3.0, 3.0)};

    writer.write_polyline(polyline1);
    writer.write_polyline(polyline2);
  }

  // Read back - should get both layers
  std::vector<Contour> contours = read_gpkg(test_file, ProgressTracker());
  EXPECT_GE(contours.size(), 2);

  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }
}

// Test GPKGWriter with different field types
TEST(GPKG, DifferentFieldTypes) {
  fs::path test_file = blaze::test::unique_test_output_path("fields", ".gpkg");

  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }

  std::string projection =
      R"(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]])";

  {
    GPKGWriter writer(test_file.string(), projection);

    Polyline polyline;
    polyline.layer = "test";
    polyline.name = "test_line";
    polyline.vertices = {Coordinate2D<double>(0.0, 0.0), Coordinate2D<double>(1.0, 1.0)};

    // Test with int, double, and string fields
    writer.write_polyline(
        polyline, {{"elevation", 100.0}, {"id", 42}, {"name_str", std::string("test_name")}});
  }

  // File should exist and be readable
  EXPECT_TRUE(fs::exists(test_file));

  std::vector<Contour> contours = read_gpkg(test_file, ProgressTracker());
  EXPECT_GE(contours.size(), 1);

  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }
}

// Test read_gpkg with non-existent file
TEST(GPKG, ReadNonExistentFile) {
  fs::path test_file = blaze::test::unique_test_output_path("nonexistent", ".gpkg");

  // Ensure file doesn't exist
  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }

  // Should throw an error for non-existent files
  EXPECT_THROW(read_gpkg(test_file, ProgressTracker()), std::runtime_error);
}

// Test read_gpkg with empty file (not a valid GPKG)
TEST(GPKG, ReadEmptyFile) {
  fs::path test_file = blaze::test::unique_test_output_path("empty", ".gpkg");

  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }

  // Create empty file (not a valid GPKG)
  std::ofstream ofs(test_file.string());
  ofs.close();

  // Should throw an error for invalid GPKG files
  EXPECT_THROW(read_gpkg(test_file, ProgressTracker()), std::runtime_error);

  if (fs::exists(test_file)) {
    fs::remove(test_file);
  }
}

TEST(GPKG, CombineGpkgs) {
  fs::path contours_file = blaze::test::unique_test_output_path("combine_contours", ".gpkg");
  fs::path streams_file = blaze::test::unique_test_output_path("combine_streams", ".gpkg");
  fs::path output_file = blaze::test::unique_test_output_path("combined_map", ".gpkg");

  std::string projection =
      R"(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]])";

  {
    GPKGWriter writer(contours_file.string(), projection, "Contour");
    writer.write_polyline(
        Polyline{.layer = "101_Contour", .name = "10", .vertices = {{0, 0}, {1, 0}, {1, 1}}},
        {{"elevation", 10.0}});
    writer.write_polyline(
        Polyline{.layer = "103_Form_Line", .name = "2.5", .vertices = {{2, 0}, {3, 0}, {3, 1}}},
        {{"elevation", 2.5}});
  }
  {
    GPKGWriter writer(streams_file.string(), projection, "streams");
    writer.write_polyline(
        Polyline{.layer = "streams", .name = "0.03", .vertices = {{0, 2}, {1, 2}, {2, 2}}},
        {{"catchment", 0.03}});
  }

  combine_gpkgs({contours_file, streams_file}, output_file, projection, ProgressTracker());
  ASSERT_TRUE(fs::exists(output_file));

  ensure_gdal_initialized();
  GDALDataset* dataset = (GDALDataset*)GDALOpenEx(output_file.string().c_str(), GDAL_OF_VECTOR,
                                                  nullptr, nullptr, nullptr);
  ASSERT_NE(dataset, nullptr);

  std::set<std::string> layer_names;
  int feature_count = 0;
  for (int i = 0; i < dataset->GetLayerCount(); i++) {
    OGRLayer* layer = dataset->GetLayer(i);
    if (!layer || layer->GetFeatureCount(false) == 0) continue;
    layer_names.insert(layer->GetName());
    feature_count += layer->GetFeatureCount(false);
  }
  GDALClose(dataset);

  EXPECT_EQ(feature_count, 3);
  EXPECT_TRUE(layer_names.contains("101_Contour"));
  EXPECT_TRUE(layer_names.contains("103_Form_Line"));
  EXPECT_TRUE(layer_names.contains("streams"));

  for (const fs::path& path : {contours_file, streams_file, output_file}) {
    if (fs::exists(path)) fs::remove(path);
  }
}

TEST(GPKG, CombineGpkgsIncludesVegetationPolygons) {
  fs::path contours_file = blaze::test::unique_test_output_path("combine_contours_vege", ".gpkg");
  fs::path vegetation_file = blaze::test::unique_test_output_path("combine_vegetation", ".gpkg");
  fs::path output_file = blaze::test::unique_test_output_path("combined_map_vege", ".gpkg");

  std::string projection =
      R"(GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]])";

  {
    GPKGWriter writer(contours_file.string(), projection, "Contour");
    writer.write_polyline(
        Polyline{.layer = "101_Contour", .name = "10", .vertices = {{0, 0}, {1, 0}, {1, 1}}},
        {{"elevation", 10.0}});
  }
  {
    GPKGWriter writer(vegetation_file.string(), projection, "vegetation");
    writer.write_polygon("406_Slow_Running", "406", {{0, 0}, {10, 0}, {10, 10}, {0, 10}}, {});
    writer.write_polygon("405_Forest", "405", {{20, 0}, {30, 0}, {30, 10}, {20, 10}}, {});
  }

  combine_gpkgs({contours_file, vegetation_file}, output_file, projection, ProgressTracker());
  ASSERT_TRUE(fs::exists(output_file));

  ensure_gdal_initialized();
  GDALDataset* dataset = (GDALDataset*)GDALOpenEx(output_file.string().c_str(), GDAL_OF_VECTOR,
                                                  nullptr, nullptr, nullptr);
  ASSERT_NE(dataset, nullptr);

  std::set<std::string> layer_names;
  for (int i = 0; i < dataset->GetLayerCount(); i++) {
    OGRLayer* layer = dataset->GetLayer(i);
    if (!layer || layer->GetFeatureCount(false) == 0) continue;
    layer_names.insert(layer->GetName());
  }
  GDALClose(dataset);

  EXPECT_TRUE(layer_names.contains("101_Contour"));
  EXPECT_TRUE(layer_names.contains("406_Slow_Running"));
  EXPECT_TRUE(layer_names.contains("405_Forest"));

  for (const fs::path& path : {contours_file, vegetation_file, output_file}) {
    if (fs::exists(path)) fs::remove(path);
  }
}