<commit_before>#include "WKTLayer.h"
#include "Config.h"
#include "Geometry.h"
#include "Hash.h"
#include "Layer.h"
#include "State.h"
#include <boost/timer/timer.hpp>
#include <ctpp2/CDT.hpp>
#include <gis/CoordinateTransformation.h>
#include <gis/OGR.h>
#include <gis/Types.h>
#include <spine/Exception.h>
#include <ogr_geometry.h>

namespace SmartMet
{
namespace Plugin
{
namespace Dali
{
// ----------------------------------------------------------------------
/*!
 * \brief Initialize from JSON
 */
// ----------------------------------------------------------------------

void WKTLayer::init(const Json::Value& theJson,
                    const State& theState,
                    const Config& theConfig,
                    const Properties& theProperties)
{
  try
  {
    if (!theJson.isObject())
      throw Spine::Exception(BCP, "WKT-layer JSON is not a JSON object");

    Layer::init(theJson, theState, theConfig, theProperties);

    precision = theConfig.defaultPrecision("wkt");

    // Extract member values

    Json::Value nulljson;

    auto json = theJson.get("wkt", nulljson);
    if (!json.isNull())
      wkt = json.asString();

    json = theJson.get("resolution", nulljson);
    if (!json.isNull())
      resolution = json.asDouble();

    json = theJson.get("relativeresolution", nulljson);
    if (!json.isNull())
      relativeresolution = json.asDouble();

    json = theJson.get("precision", nulljson);
    if (!json.isNull())
      precision = json.asDouble();
  }
  catch (...)
  {
    throw Spine::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Generate the layer details into the template hash
 */
// ----------------------------------------------------------------------

void WKTLayer::generate(CTPP::CDT& theGlobals, CTPP::CDT& theLayersCdt, State& theState)
{
  try
  {
    if (wkt.empty())
      throw Spine::Exception(BCP, "WKTLayer WKT must be defined and be non-empty");

    if (!validLayer(theState))
      return;

    if (resolution && relativeresolution)
      throw Spine::Exception(BCP, "Cannot set both resolution and relativeresolution for WKT");

    std::string report = "WKTLayer::generate finished in %t sec CPU, %w sec real\n";
    boost::movelib::unique_ptr<boost::timer::auto_cpu_timer> timer;
    if (theState.useTimer())
      timer = boost::movelib::make_unique<boost::timer::auto_cpu_timer>(2, report);

    // Get projection details

    auto q = getModel(theState);

    projection.update(q);
    const auto& crs = projection.getCRS();
    const auto& box = projection.getBox();

    // And the box needed for clipping
    const auto clipbox = getClipBox(box);

    // Create the shape
    Fmi::SpatialReference wgs84("WGS84");

    char* cwkt = const_cast<char*>(wkt.c_str());  // NOLINT(cppcoreguidelines-pro-type-const-cast)
    OGRGeometry* ogeom = nullptr;
    OGRErr err = OGRGeometryFactory::createFromWkt(&cwkt, wgs84, &ogeom);

    if (err != OGRERR_NONE)
    {
      // delete wgs84;  would segfault
      throw SmartMet::Spine::Exception(BCP, "Failed to convert WKT to OGRGeometry");
    }

    if (wgs84.isAxisSwapped())
      ogeom->swapXY();

    OGRGeometryPtr geom(ogeom);

    // Resample to get more accuracy if so requested

    if (resolution || relativeresolution)
    {
      if (!projection.resolution)
        throw Spine::Exception(BCP,
                               "Cannot segmentize WKT layer if projection resolution is not known");

      double res = 0;
      if (resolution)
        res = *resolution;
      else
        res = (*projection.resolution) * (*relativeresolution);

      // Convert resolution in km to resolution in degrees (approximation only)

      double pi = boost::math::constants::pi<double>();
      double circumference = 2 * pi * 6371.220;  // km
      double delta = 360 * res / circumference;  // part of the earth circumference in

      geom->segmentize(delta);
    }

    // Establish coordinate transformation from WGS84 to image

    Fmi::CoordinateTransformation transformation(wgs84, *crs);

    geom.reset(transformation.transformGeometry(*geom));

    if (!geom || geom->IsEmpty())
      return;

    // Clip the geometry to the bounding box

    geom.reset(Fmi::OGR::polyclip(*geom, clipbox));
    if (!geom || geom->IsEmpty())
      return;

    // Update the globals

    if (css)
    {
      std::string name = theState.getCustomer() + "/" + *css;
      theGlobals["css"][name] = theState.getStyle(*css);
    }

    std::string iri = qid;
    if (iri.empty())
      iri = theState.generateUniqueId();
    else if (!theState.addId(iri))
      throw Spine::Exception(BCP, "Non-unique ID assigned to WKT layer").addParameter("ID", iri);

    CTPP::CDT wkt_cdt(CTPP::CDT::HASH_VAL);
    wkt_cdt["iri"] = iri;
    wkt_cdt["data"] = Geometry::toString(*geom, theState.getType(), box, crs, precision);<commit_msg>Added WKT segmentation during coordinate transformation<commit_after>#include "WKTLayer.h"
#include "Config.h"
#include "Geometry.h"
#include "Hash.h"
#include "Layer.h"
#include "State.h"
#include <boost/timer/timer.hpp>
#include <ctpp2/CDT.hpp>
#include <gis/CoordinateTransformation.h>
#include <gis/OGR.h>
#include <gis/Types.h>
#include <spine/Exception.h>
#include <ogr_geometry.h>

namespace SmartMet
{
namespace Plugin
{
namespace Dali
{
// ----------------------------------------------------------------------
/*!
 * \brief Initialize from JSON
 */
// ----------------------------------------------------------------------

void WKTLayer::init(const Json::Value& theJson,
                    const State& theState,
                    const Config& theConfig,
                    const Properties& theProperties)
{
  try
  {
    if (!theJson.isObject())
      throw Spine::Exception(BCP, "WKT-layer JSON is not a JSON object");

    Layer::init(theJson, theState, theConfig, theProperties);

    precision = theConfig.defaultPrecision("wkt");

    // Extract member values

    Json::Value nulljson;

    auto json = theJson.get("wkt", nulljson);
    if (!json.isNull())
      wkt = json.asString();

    json = theJson.get("resolution", nulljson);
    if (!json.isNull())
      resolution = json.asDouble();

    json = theJson.get("relativeresolution", nulljson);
    if (!json.isNull())
      relativeresolution = json.asDouble();

    json = theJson.get("precision", nulljson);
    if (!json.isNull())
      precision = json.asDouble();
  }
  catch (...)
  {
    throw Spine::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Generate the layer details into the template hash
 */
// ----------------------------------------------------------------------

void WKTLayer::generate(CTPP::CDT& theGlobals, CTPP::CDT& theLayersCdt, State& theState)
{
  try
  {
    if (wkt.empty())
      throw Spine::Exception(BCP, "WKTLayer WKT must be defined and be non-empty");

    if (!validLayer(theState))
      return;

    if (resolution && relativeresolution)
      throw Spine::Exception(BCP, "Cannot set both resolution and relativeresolution for WKT");

    std::string report = "WKTLayer::generate finished in %t sec CPU, %w sec real\n";
    boost::movelib::unique_ptr<boost::timer::auto_cpu_timer> timer;
    if (theState.useTimer())
      timer = boost::movelib::make_unique<boost::timer::auto_cpu_timer>(2, report);

    // Get projection details

    auto q = getModel(theState);

    projection.update(q);
    const auto& crs = projection.getCRS();
    const auto& box = projection.getBox();

    // And the box needed for clipping
    const auto clipbox = getClipBox(box);

    // Create the shape
    Fmi::SpatialReference wgs84("WGS84");

    char* cwkt = const_cast<char*>(wkt.c_str());  // NOLINT(cppcoreguidelines-pro-type-const-cast)
    OGRGeometry* ogeom = nullptr;
    OGRErr err = OGRGeometryFactory::createFromWkt(&cwkt, wgs84, &ogeom);

    if (err != OGRERR_NONE)
    {
      // delete wgs84;  would segfault
      throw SmartMet::Spine::Exception(BCP, "Failed to convert WKT to OGRGeometry");
    }

    if (wgs84.isAxisSwapped())
      ogeom->swapXY();

    OGRGeometryPtr geom(ogeom);

    // Resample to get more accuracy if so requested

    if (resolution || relativeresolution)
    {
      if (!projection.resolution)
        throw Spine::Exception(BCP,
                               "Cannot segmentize WKT layer if projection resolution is not known");

      double res = 0;
      if (resolution)
        res = *resolution;
      else
        res = (*projection.resolution) * (*relativeresolution);

      // Convert resolution in km to resolution in degrees (approximation only)

      double pi = boost::math::constants::pi<double>();
      double circumference = 2 * pi * 6371.220;  // km
      double delta = 360 * res / circumference;  // part of the earth circumference in

      geom->segmentize(delta);
    }

    // Establish coordinate transformation from WGS84 to image

    Fmi::CoordinateTransformation transformation(wgs84, *crs);

    // Perhaps this should be customizable instead of being fixed to one degree?
    const double maximum_segment_length = 1;
    geom.reset(transformation.transformGeometry(*geom, maximum_segment_length));

    if (!geom || geom->IsEmpty())
      return;

    // Clip the geometry to the bounding box

    geom.reset(Fmi::OGR::polyclip(*geom, clipbox));
    if (!geom || geom->IsEmpty())
      return;

    // Update the globals

    if (css)
    {
      std::string name = theState.getCustomer() + "/" + *css;
      theGlobals["css"][name] = theState.getStyle(*css);
    }

    std::string iri = qid;
    if (iri.empty())
      iri = theState.generateUniqueId();
    else if (!theState.addId(iri))
      throw Spine::Exception(BCP, "Non-unique ID assigned to WKT layer").addParameter("ID", iri);

    CTPP::CDT wkt_cdt(CTPP::CDT::HASH_VAL);
    wkt_cdt["iri"] = iri;
    wkt_cdt["data"] = Geometry::toString(*geom, theState.getType(), box, crs, precision);
{'fraction_non_alphanumeric': 0.18434644417957885, 'fraction_numerical': 0.00566150178784267, 'mean_word_length': 2.5095852213314744, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4465101', 'n_tokens_mistral': 3203, 'n_tokens_neox': 2985, 'n_words': 757}