add a geojson debugger that allows creating features for easy inspection
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
#ifndef OSRM_GEOJSON_DEBUG_LOGGER_HPP
|
||||
#define OSRM_GEOJSON_DEBUG_LOGGER_HPP
|
||||
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "util/json_container.hpp"
|
||||
#include "util/json_renderer.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
// in case we want to do scenario-based logging, we can specify a dedicated logging scenario to be
|
||||
// able to use multiple files for the same converter
|
||||
enum class LoggingScenario
|
||||
{
|
||||
eDefault = 0
|
||||
};
|
||||
|
||||
// forward declaration to become friends
|
||||
template <class geojson_conversion_policy, LoggingScenario scenario> class ScopedGeojsonLoggerGuard;
|
||||
|
||||
// a geojson logger requires a conversion policy to transfer arbitrary input into viable geojson.
|
||||
// features of the same kind are stored in the same geojson file
|
||||
template <class geojson_conversion_policy, LoggingScenario scenario = LoggingScenario::eDefault>
|
||||
class GeojsonLogger
|
||||
{
|
||||
// become friends with the guard
|
||||
friend class ScopedGeojsonLoggerGuard<geojson_conversion_policy, scenario>;
|
||||
|
||||
// having these private enforces the guard to be used to initialise/close/write
|
||||
private:
|
||||
// cannot lock, is tooling for locked function
|
||||
static void output(bool first, const util::json::Object &object)
|
||||
{
|
||||
if (!first)
|
||||
ofs << ",\n\t\t";
|
||||
// objects are simply forwarded
|
||||
util::json::render(ofs, object);
|
||||
}
|
||||
|
||||
// cannot lock, is tooling for locked function
|
||||
static void output(bool first, const util::json::Array &array)
|
||||
{
|
||||
for (const auto object : array.values)
|
||||
{
|
||||
if (!first)
|
||||
ofs << ",\n\t\t";
|
||||
|
||||
util::json::render(ofs, object.get<util::json::Object>());
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
// writes a single feature into the Geojson file
|
||||
template <typename... Args> static bool Write(Args &&... args)
|
||||
{
|
||||
// make sure to syncronize logging output, our writing should be sequential
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
|
||||
// if there is no logfile, we cannot write
|
||||
if (!ofs.is_open() || (nullptr == policy))
|
||||
{
|
||||
// this can only happend between two guards when concurrent writing occurs
|
||||
return false;
|
||||
}
|
||||
|
||||
// use our policy to convert the arguments into geojson, this can be done in parallel
|
||||
const auto json_object = (*policy)(std::forward<Args>(args)...);
|
||||
|
||||
// different features are separated by `,`
|
||||
// since we are not building a full json collection of features, we have to do some of the
|
||||
// bookeeping ourselves. This prevens us from doing one huge json object, if we are
|
||||
// processing larger results
|
||||
output(first, json_object);
|
||||
first = false;
|
||||
|
||||
return static_cast<bool>(ofs);
|
||||
}
|
||||
|
||||
// Opening a logger, we initialize a geojson feature collection
|
||||
// to be later filled with additional data
|
||||
static bool Open(const std::string &logfile)
|
||||
{
|
||||
// if a file is open, close it off. When this function returns, there should be an open
|
||||
// logfile. However, there is a brief period between closing and openen where we could miss
|
||||
// out on log output. Such a sad life
|
||||
if (ofs.is_open())
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING)
|
||||
<< "Overwriting " << logfile
|
||||
<< ". Is this desired behaviour? If this message occurs more than once rethink the "
|
||||
"location of your Logger Guard.";
|
||||
Close();
|
||||
}
|
||||
|
||||
// make sure to syncronize logging output, cannot be locked earlier, since Close also locks
|
||||
// and we don't want deadlocks
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
ofs.open(logfile, std::ios::binary);
|
||||
|
||||
// set up a feature collection
|
||||
ofs << "{\n\t\"type\": \"FeatureCollection\",\n\t\"features\": [\n\t";
|
||||
// remember whether we need to output a colon
|
||||
first = true;
|
||||
|
||||
return static_cast<bool>(ofs);
|
||||
}
|
||||
|
||||
// finalising touches on the GeoJson
|
||||
static bool Close()
|
||||
{
|
||||
// make sure to syncronize logging output
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
|
||||
// finishe the geojson feature collection and close it all off
|
||||
if (ofs.is_open())
|
||||
{
|
||||
ofs << "\n\t]\n}";
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
return static_cast<bool>(ofs);
|
||||
}
|
||||
|
||||
static void SetPolicy(geojson_conversion_policy *new_policy) { policy = new_policy; }
|
||||
|
||||
private:
|
||||
static bool first;
|
||||
static std::mutex lock;
|
||||
static std::ofstream ofs;
|
||||
|
||||
static geojson_conversion_policy *policy;
|
||||
};
|
||||
|
||||
// make sure to do opening and closing of our guard
|
||||
template <class geojson_conversion_policy, LoggingScenario scenario = LoggingScenario::eDefault>
|
||||
class ScopedGeojsonLoggerGuard
|
||||
{
|
||||
public:
|
||||
template <typename... Args>
|
||||
ScopedGeojsonLoggerGuard(const std::string &logfile, Args &&... args)
|
||||
: policy(std::forward<Args>(args)...)
|
||||
{
|
||||
GeojsonLogger<geojson_conversion_policy, scenario>::Open(logfile);
|
||||
GeojsonLogger<geojson_conversion_policy, scenario>::SetPolicy(&policy);
|
||||
}
|
||||
|
||||
~ScopedGeojsonLoggerGuard()
|
||||
{
|
||||
GeojsonLogger<geojson_conversion_policy, scenario>::Close();
|
||||
GeojsonLogger<geojson_conversion_policy, scenario>::SetPolicy(nullptr);
|
||||
}
|
||||
|
||||
template <typename... Args> static bool Write(Args &&... args)
|
||||
{
|
||||
return GeojsonLogger<geojson_conversion_policy, scenario>::Write(
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
geojson_conversion_policy policy;
|
||||
};
|
||||
|
||||
template <class geojson_conversion_policy, LoggingScenario scenario>
|
||||
bool GeojsonLogger<geojson_conversion_policy, scenario>::first;
|
||||
|
||||
template <class geojson_conversion_policy, LoggingScenario scenario>
|
||||
std::mutex GeojsonLogger<geojson_conversion_policy, scenario>::lock;
|
||||
|
||||
template <class geojson_conversion_policy, LoggingScenario scenario>
|
||||
std::ofstream GeojsonLogger<geojson_conversion_policy, scenario>::ofs;
|
||||
|
||||
template <class geojson_conversion_policy, LoggingScenario scenario>
|
||||
geojson_conversion_policy *GeojsonLogger<geojson_conversion_policy, scenario>::policy;
|
||||
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_GEOJSON_DEBUG_LOGGER_HPP */
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef OSRM_GEOJSON_DEBUG_POLICIES
|
||||
#define OSRM_GEOJSON_DEBUG_POLICIES
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
struct NodeIdVectorToLineString
|
||||
{
|
||||
NodeIdVectorToLineString(const std::vector<extractor::QueryNode> &node_coordinates);
|
||||
|
||||
// converts a vector of node ids into a linestring geojson feature
|
||||
util::json::Object operator()(const std::vector<NodeID> &node_ids,
|
||||
const boost::optional<json::Object> &properties = {}) const;
|
||||
|
||||
const std::vector<extractor::QueryNode> &node_coordinates;
|
||||
};
|
||||
|
||||
struct CoordinateVectorToLineString
|
||||
{
|
||||
// converts a vector of node ids into a linestring geojson feature
|
||||
util::json::Object operator()(const std::vector<util::Coordinate> &coordinates,
|
||||
const boost::optional<json::Object> &properties = {}) const;
|
||||
};
|
||||
|
||||
struct NodeIdVectorToMultiPoint
|
||||
{
|
||||
NodeIdVectorToMultiPoint(const std::vector<extractor::QueryNode> &node_coordinates);
|
||||
|
||||
// converts a vector of node ids into a linestring geojson feature
|
||||
util::json::Object operator()(const std::vector<NodeID> &node_ids,
|
||||
const boost::optional<json::Object> &properties = {}) const;
|
||||
|
||||
const std::vector<extractor::QueryNode> &node_coordinates;
|
||||
};
|
||||
|
||||
struct CoordinateVectorToMultiPoint
|
||||
{
|
||||
// converts a vector of node ids into a linestring geojson feature
|
||||
util::json::Object operator()(const std::vector<util::Coordinate> &coordinates,
|
||||
const boost::optional<json::Object> &properties = {}) const;
|
||||
};
|
||||
|
||||
} /* namespace util */
|
||||
} /* namespace osrm */
|
||||
|
||||
#endif /* OSRM_GEOJSON_DEBUG_POLICIES */
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef OSRM_GEOJSON_DEBUG_POLICY_TOOLKIT_HPP
|
||||
#define OSRM_GEOJSON_DEBUG_POLICY_TOOLKIT_HPP
|
||||
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
enum GeojsonStyleSize
|
||||
{
|
||||
tiny,
|
||||
small,
|
||||
medium,
|
||||
large,
|
||||
extra_large,
|
||||
num_styles
|
||||
};
|
||||
|
||||
enum GeojsonStyleColors
|
||||
{
|
||||
red,
|
||||
purple,
|
||||
blue,
|
||||
green,
|
||||
yellow,
|
||||
cyan,
|
||||
brown,
|
||||
pink,
|
||||
num_colors
|
||||
};
|
||||
|
||||
const constexpr char *geojson_debug_predifined_colors[GeojsonStyleColors::num_colors] = {
|
||||
"#FF4848", "#800080", "#5757FF", "#1FCB4A", "#FFE920", "#29AFD6", "#B05F3C", "#FE67EB"};
|
||||
|
||||
const constexpr double geojson_predefined_sizes[GeojsonStyleSize::num_styles] = {
|
||||
2.0, 3.5, 5.0, 6.5, 8};
|
||||
|
||||
inline util::json::Object makeStyle(const GeojsonStyleSize size_type,
|
||||
const GeojsonStyleColors predefined_color)
|
||||
{
|
||||
util::json::Object style;
|
||||
// style everything, since we don't know the feature type
|
||||
style.values["stroke"] = geojson_debug_predifined_colors[predefined_color];
|
||||
style.values["circle-color"] = geojson_debug_predifined_colors[predefined_color];
|
||||
style.values["line-width"] = geojson_predefined_sizes[size_type];
|
||||
style.values["circle-radius"] = geojson_predefined_sizes[size_type];
|
||||
return style;
|
||||
}
|
||||
|
||||
struct CoordinateToJsonArray
|
||||
{
|
||||
util::json::Array operator()(const util::Coordinate coordinate)
|
||||
{
|
||||
util::json::Array json_coordinate;
|
||||
json_coordinate.values.push_back(static_cast<double>(toFloating(coordinate.lon)));
|
||||
json_coordinate.values.push_back(static_cast<double>(toFloating(coordinate.lat)));
|
||||
return json_coordinate;
|
||||
}
|
||||
};
|
||||
|
||||
struct NodeIdToCoordinate
|
||||
{
|
||||
NodeIdToCoordinate(const std::vector<extractor::QueryNode> &node_coordinates)
|
||||
: node_coordinates(node_coordinates)
|
||||
{
|
||||
}
|
||||
|
||||
const std::vector<extractor::QueryNode> &node_coordinates;
|
||||
|
||||
util::json::Array operator()(const NodeID nid)
|
||||
{
|
||||
auto coordinate = node_coordinates[nid];
|
||||
CoordinateToJsonArray converter;
|
||||
return converter(coordinate);
|
||||
}
|
||||
};
|
||||
|
||||
inline util::json::Object makeFeature(std::string type,
|
||||
util::json::Array coordinates,
|
||||
const boost::optional<util::json::Object> &properties = {})
|
||||
{
|
||||
util::json::Object result;
|
||||
result.values["type"] = "Feature";
|
||||
result.values["properties"] = properties ? *properties : util::json::Object();
|
||||
util::json::Object geometry;
|
||||
geometry.values["type"] = std::move(type);
|
||||
geometry.values["properties"] = util::json::Object();
|
||||
geometry.values["coordinates"] = std::move(coordinates);
|
||||
result.values["geometry"] = geometry;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline util::json::Array makeJsonArray(const std::vector<util::Coordinate> &input_coordinates)
|
||||
{
|
||||
util::json::Array coordinates;
|
||||
std::transform(input_coordinates.begin(),
|
||||
input_coordinates.end(),
|
||||
std::back_inserter(coordinates.values),
|
||||
CoordinateToJsonArray());
|
||||
return coordinates;
|
||||
}
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_GEOJSON_DEBUG_POLICY_TOOLKIT_HPP */
|
||||
Reference in New Issue
Block a user